xref: /petsc/src/ksp/pc/impls/hpddm/pchpddm.cxx (revision 2cdf5ea42bccd4e651ec69c5d7cf37657be83b41)
1 #include <petscsf.h>
2 #include <petsc/private/vecimpl.h>
3 #include <petsc/private/matimpl.h>
4 #include <petsc/private/petschpddm.h> /*I "petscpc.h" I*/
5 #include <petsc/private/pcimpl.h>
6 #include <petsc/private/dmimpl.h> /* this must be included after petschpddm.h so that DM_MAX_WORK_VECTORS is not defined  */
7                                   /* otherwise, it is assumed that one is compiling libhpddm_petsc => circular dependency */
8 
9 static PetscErrorCode (*loadedSym)(HPDDM::Schwarz<PetscScalar> *const, IS, Mat, Mat, Mat, std::vector<Vec>, PC_HPDDM_Level **const) = nullptr;
10 
11 static PetscBool PCHPDDMPackageInitialized = PETSC_FALSE;
12 
13 PetscLogEvent PC_HPDDM_Strc;
14 PetscLogEvent PC_HPDDM_PtAP;
15 PetscLogEvent PC_HPDDM_PtBP;
16 PetscLogEvent PC_HPDDM_Next;
17 PetscLogEvent PC_HPDDM_SetUp[PETSC_PCHPDDM_MAXLEVELS];
18 PetscLogEvent PC_HPDDM_Solve[PETSC_PCHPDDM_MAXLEVELS];
19 
20 const char *const PCHPDDMCoarseCorrectionTypes[] = {"DEFLATED", "ADDITIVE", "BALANCED", "NONE", "PCHPDDMCoarseCorrectionType", "PC_HPDDM_COARSE_CORRECTION_", nullptr};
21 const char *const PCHPDDMSchurPreTypes[]         = {"LEAST_SQUARES", "GENEO", "PCHPDDMSchurPreType", "PC_HPDDM_SCHUR_PRE", nullptr};
22 
23 static PetscErrorCode PCReset_HPDDM(PC pc)
24 {
25   PC_HPDDM *data = (PC_HPDDM *)pc->data;
26 
27   PetscFunctionBegin;
28   if (data->levels) {
29     for (PetscInt i = 0; i < PETSC_PCHPDDM_MAXLEVELS && data->levels[i]; ++i) {
30       PetscCall(KSPDestroy(&data->levels[i]->ksp));
31       PetscCall(PCDestroy(&data->levels[i]->pc));
32       PetscCall(PetscFree(data->levels[i]));
33     }
34     PetscCall(PetscFree(data->levels));
35   }
36   PetscCall(ISDestroy(&data->is));
37   PetscCall(MatDestroy(&data->aux));
38   PetscCall(MatDestroy(&data->B));
39   PetscCall(VecDestroy(&data->normal));
40   data->correction = PC_HPDDM_COARSE_CORRECTION_DEFLATED;
41   data->Neumann    = PETSC_BOOL3_UNKNOWN;
42   data->deflation  = PETSC_FALSE;
43   data->setup      = nullptr;
44   data->setup_ctx  = nullptr;
45   PetscFunctionReturn(PETSC_SUCCESS);
46 }
47 
48 static PetscErrorCode PCDestroy_HPDDM(PC pc)
49 {
50   PC_HPDDM *data = (PC_HPDDM *)pc->data;
51 
52   PetscFunctionBegin;
53   PetscCall(PCReset_HPDDM(pc));
54   PetscCall(PetscFree(data));
55   PetscCall(PetscObjectChangeTypeName((PetscObject)pc, nullptr));
56   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCHPDDMSetAuxiliaryMat_C", nullptr));
57   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCHPDDMHasNeumannMat_C", nullptr));
58   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCHPDDMSetRHSMat_C", nullptr));
59   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCHPDDMSetCoarseCorrectionType_C", nullptr));
60   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCHPDDMGetCoarseCorrectionType_C", nullptr));
61   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCHPDDMSetSTShareSubKSP_C", nullptr));
62   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCHPDDMGetSTShareSubKSP_C", nullptr));
63   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCHPDDMSetDeflationMat_C", nullptr));
64   PetscCall(PetscObjectCompose((PetscObject)pc, "_PCHPDDM_Schur", nullptr));
65   PetscFunctionReturn(PETSC_SUCCESS);
66 }
67 
68 static inline PetscErrorCode PCHPDDMSetAuxiliaryMat_Private(PC pc, IS is, Mat A, PetscBool deflation)
69 {
70   PC_HPDDM                   *data = (PC_HPDDM *)pc->data;
71   PCHPDDMCoarseCorrectionType type = data->correction;
72 
73   PetscFunctionBegin;
74   PetscValidLogicalCollectiveBool(pc, deflation, 4);
75   if (is && A) {
76     PetscInt m[2];
77 
78     PetscCall(ISGetLocalSize(is, m));
79     PetscCall(MatGetLocalSize(A, m + 1, nullptr));
80     PetscCheck(m[0] == m[1], PETSC_COMM_SELF, PETSC_ERR_USER_INPUT, "Inconsistent IS and Mat sizes (%" PetscInt_FMT " v. %" PetscInt_FMT ")", m[0], m[1]);
81   }
82   if (is) {
83     PetscCall(PetscObjectReference((PetscObject)is));
84     if (data->is) { /* new overlap definition resets the PC */
85       PetscCall(PCReset_HPDDM(pc));
86       pc->setfromoptionscalled = 0;
87       pc->setupcalled          = 0;
88       data->correction         = type;
89     }
90     PetscCall(ISDestroy(&data->is));
91     data->is = is;
92   }
93   if (A) {
94     PetscCall(PetscObjectReference((PetscObject)A));
95     PetscCall(MatDestroy(&data->aux));
96     data->aux = A;
97   }
98   data->deflation = deflation;
99   PetscFunctionReturn(PETSC_SUCCESS);
100 }
101 
102 static inline PetscErrorCode PCHPDDMSplittingMatNormal_Private(Mat A, IS *is, Mat *splitting[])
103 {
104   Mat *sub;
105   IS   zero;
106 
107   PetscFunctionBegin;
108   PetscCall(MatSetOption(A, MAT_SUBMAT_SINGLEIS, PETSC_TRUE));
109   PetscCall(MatCreateSubMatrices(A, 1, is + 2, is, MAT_INITIAL_MATRIX, splitting));
110   PetscCall(MatCreateSubMatrices(**splitting, 1, is + 2, is + 1, MAT_INITIAL_MATRIX, &sub));
111   PetscCall(MatFindZeroRows(*sub, &zero));
112   PetscCall(MatDestroySubMatrices(1, &sub));
113   PetscCall(MatSetOption(**splitting, MAT_KEEP_NONZERO_PATTERN, PETSC_TRUE));
114   PetscCall(MatZeroRowsIS(**splitting, zero, 0.0, nullptr, nullptr));
115   PetscCall(ISDestroy(&zero));
116   PetscFunctionReturn(PETSC_SUCCESS);
117 }
118 
119 static inline PetscErrorCode PCHPDDMSetAuxiliaryMatNormal_Private(PC pc, Mat A, Mat N, Mat *B, const char *pcpre, Vec *diagonal = nullptr, Mat B01 = nullptr)
120 {
121   PC_HPDDM *data         = (PC_HPDDM *)pc->data;
122   Mat      *splitting[2] = {}, aux;
123   Vec       d;
124   IS        is[3];
125   PetscReal norm;
126   PetscBool flg;
127   char      type[256] = {}; /* same size as in src/ksp/pc/interface/pcset.c */
128 
129   PetscFunctionBegin;
130   if (!B01) PetscCall(MatConvert(N, MATAIJ, MAT_INITIAL_MATRIX, B));
131   else PetscCall(MatTransposeMatMult(B01, A, MAT_INITIAL_MATRIX, PETSC_DETERMINE, B));
132   PetscCall(MatEliminateZeros(*B, PETSC_TRUE));
133   PetscCall(ISCreateStride(PETSC_COMM_SELF, A->cmap->n, A->cmap->rstart, 1, is));
134   PetscCall(MatIncreaseOverlap(*B, 1, is, 1));
135   PetscCall(ISCreateStride(PETSC_COMM_SELF, A->cmap->n, A->cmap->rstart, 1, is + 2));
136   PetscCall(ISEmbed(is[0], is[2], PETSC_TRUE, is + 1));
137   PetscCall(ISDestroy(is + 2));
138   PetscCall(ISCreateStride(PETSC_COMM_SELF, A->rmap->N, 0, 1, is + 2));
139   PetscCall(PCHPDDMSplittingMatNormal_Private(A, is, &splitting[0]));
140   if (B01) {
141     PetscCall(PCHPDDMSplittingMatNormal_Private(B01, is, &splitting[1]));
142     PetscCall(MatDestroy(&B01));
143   }
144   PetscCall(ISDestroy(is + 2));
145   PetscCall(ISDestroy(is + 1));
146   PetscCall(PetscOptionsGetString(nullptr, pcpre, "-pc_hpddm_levels_1_sub_pc_type", type, sizeof(type), nullptr));
147   PetscCall(PetscStrcmp(type, PCQR, &flg));
148   if (!flg) {
149     Mat conjugate = *splitting[splitting[1] ? 1 : 0];
150 
151     if (PetscDefined(USE_COMPLEX) && !splitting[1]) {
152       PetscCall(MatDuplicate(*splitting[0], MAT_COPY_VALUES, &conjugate));
153       PetscCall(MatConjugate(conjugate));
154     }
155     PetscCall(MatTransposeMatMult(conjugate, *splitting[0], MAT_INITIAL_MATRIX, PETSC_DETERMINE, &aux));
156     if (PetscDefined(USE_COMPLEX) && !splitting[1]) PetscCall(MatDestroy(&conjugate));
157     else if (splitting[1]) PetscCall(MatDestroySubMatrices(1, &splitting[1]));
158     PetscCall(MatNorm(aux, NORM_FROBENIUS, &norm));
159     PetscCall(MatSetOption(aux, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_FALSE));
160     if (diagonal) {
161       PetscReal norm;
162 
163       PetscCall(VecScale(*diagonal, -1.0));
164       PetscCall(VecNorm(*diagonal, NORM_INFINITY, &norm));
165       if (norm > PETSC_SMALL) {
166         PetscSF  scatter;
167         PetscInt n;
168 
169         PetscCall(ISGetLocalSize(*is, &n));
170         PetscCall(VecCreateMPI(PetscObjectComm((PetscObject)pc), n, PETSC_DECIDE, &d));
171         PetscCall(VecScatterCreate(*diagonal, *is, d, nullptr, &scatter));
172         PetscCall(VecScatterBegin(scatter, *diagonal, d, INSERT_VALUES, SCATTER_FORWARD));
173         PetscCall(VecScatterEnd(scatter, *diagonal, d, INSERT_VALUES, SCATTER_FORWARD));
174         PetscCall(PetscSFDestroy(&scatter));
175         PetscCall(MatDiagonalSet(aux, d, ADD_VALUES));
176         PetscCall(VecDestroy(&d));
177       } else PetscCall(VecDestroy(diagonal));
178     }
179     if (!diagonal) PetscCall(MatShift(aux, PETSC_SMALL * norm));
180   } else {
181     PetscBool flg;
182 
183     PetscCheck(!splitting[1], PetscObjectComm((PetscObject)pc), PETSC_ERR_SUP, "Cannot use PCQR when A01 != A10^T");
184     if (diagonal) {
185       PetscCall(VecNorm(*diagonal, NORM_INFINITY, &norm));
186       PetscCheck(norm < PETSC_SMALL, PetscObjectComm((PetscObject)pc), PETSC_ERR_SUP, "Nonzero diagonal A11 block");
187       PetscCall(VecDestroy(diagonal));
188     }
189     PetscCall(PetscObjectTypeCompare((PetscObject)N, MATNORMAL, &flg));
190     if (flg) PetscCall(MatCreateNormal(*splitting[0], &aux));
191     else PetscCall(MatCreateNormalHermitian(*splitting[0], &aux));
192   }
193   PetscCall(MatDestroySubMatrices(1, &splitting[0]));
194   PetscCall(PCHPDDMSetAuxiliaryMat(pc, *is, aux, nullptr, nullptr));
195   data->Neumann = PETSC_BOOL3_TRUE;
196   PetscCall(ISDestroy(is));
197   PetscCall(MatDestroy(&aux));
198   PetscFunctionReturn(PETSC_SUCCESS);
199 }
200 
201 static PetscErrorCode PCHPDDMSetAuxiliaryMat_HPDDM(PC pc, IS is, Mat A, PetscErrorCode (*setup)(Mat, PetscReal, Vec, Vec, PetscReal, IS, void *), void *setup_ctx)
202 {
203   PC_HPDDM *data = (PC_HPDDM *)pc->data;
204 
205   PetscFunctionBegin;
206   PetscCall(PCHPDDMSetAuxiliaryMat_Private(pc, is, A, PETSC_FALSE));
207   if (setup) {
208     data->setup     = setup;
209     data->setup_ctx = setup_ctx;
210   }
211   PetscFunctionReturn(PETSC_SUCCESS);
212 }
213 
214 typedef struct {
215   KSP      ksp;
216   PetscInt its;
217 } PC_KSP;
218 
219 static inline PetscErrorCode PCSetUp_KSP_Private(PC pc)
220 {
221   PC_KSP           *data   = (PC_KSP *)pc->data;
222   const std::string prefix = ((PetscObject)data->ksp)->prefix;
223   std::string       sub;
224 
225   PetscFunctionBegin;
226   PetscCheck(prefix.size() >= 9, PETSC_COMM_SELF, PETSC_ERR_PLIB, "The prefix of this PCKSP should be of length at least 9 to hold the suffix pc_hpddm_");
227   sub = prefix.substr(0, prefix.size() - 9);
228   PetscCall(PCSetType(pc, PCHPDDM));
229   PetscCall(PCSetOptionsPrefix(pc, sub.c_str()));
230   PetscFunctionReturn(PETSC_SUCCESS);
231 }
232 
233 static PetscErrorCode PCSetUp_KSP(PC pc)
234 {
235   PetscFunctionBegin;
236   PetscCall(PCSetUp_KSP_Private(pc));
237   PetscCall(PCSetFromOptions(pc));
238   PetscCall(PCSetUp(pc));
239   PetscFunctionReturn(PETSC_SUCCESS);
240 }
241 
242 /*@C
243   PCHPDDMSetAuxiliaryMat - Sets the auxiliary matrix used by `PCHPDDM` for the concurrent GenEO problems at the finest level.
244 
245   Input Parameters:
246 + pc    - preconditioner context
247 . is    - index set of the local auxiliary, e.g., Neumann, matrix
248 . A     - auxiliary sequential matrix
249 . setup - function for generating the auxiliary matrix entries, may be `NULL`
250 - ctx   - context for `setup`, may be `NULL`
251 
252   Calling sequence of `setup`:
253 + J   - matrix whose values are to be set
254 . t   - time
255 . X   - linearization point
256 . X_t - time-derivative of the linearization point
257 . s   - step
258 . ovl - index set of the local auxiliary, e.g., Neumann, matrix
259 - ctx - context for `setup`, may be `NULL`
260 
261   Level: intermediate
262 
263   Note:
264   As an example, in a finite element context with nonoverlapping subdomains plus (overlapping) ghost elements, this could be the unassembled (Neumann)
265   local overlapping operator. As opposed to the assembled (Dirichlet) local overlapping operator obtained by summing neighborhood contributions
266   at the interface of ghost elements.
267 
268   Fortran Notes:
269   Only `PETSC_NULL_FUNCTION` is supported for `setup` and `ctx` is never accessed
270 
271 .seealso: [](ch_ksp), `PCHPDDM`, `PCCreate()`, `PCSetType()`, `PCType`, `PC`, `PCHPDDMSetRHSMat()`, `MATIS`
272 @*/
273 PetscErrorCode PCHPDDMSetAuxiliaryMat(PC pc, IS is, Mat A, PetscErrorCode (*setup)(Mat J, PetscReal t, Vec X, Vec X_t, PetscReal s, IS ovl, void *ctx), void *ctx)
274 {
275   PetscFunctionBegin;
276   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
277   if (is) PetscValidHeaderSpecific(is, IS_CLASSID, 2);
278   if (A) PetscValidHeaderSpecific(A, MAT_CLASSID, 3);
279   if (pc->ops->setup == PCSetUp_KSP) PetscCall(PCSetUp_KSP_Private(pc));
280   PetscTryMethod(pc, "PCHPDDMSetAuxiliaryMat_C", (PC, IS, Mat, PetscErrorCode (*)(Mat, PetscReal, Vec, Vec, PetscReal, IS, void *), void *), (pc, is, A, setup, ctx));
281   PetscFunctionReturn(PETSC_SUCCESS);
282 }
283 
284 static PetscErrorCode PCHPDDMHasNeumannMat_HPDDM(PC pc, PetscBool has)
285 {
286   PC_HPDDM *data = (PC_HPDDM *)pc->data;
287 
288   PetscFunctionBegin;
289   data->Neumann = PetscBoolToBool3(has);
290   PetscFunctionReturn(PETSC_SUCCESS);
291 }
292 
293 /*@
294   PCHPDDMHasNeumannMat - Informs `PCHPDDM` that the `Mat` passed to `PCHPDDMSetAuxiliaryMat()` is the local Neumann matrix.
295 
296   Input Parameters:
297 + pc  - preconditioner context
298 - has - Boolean value
299 
300   Level: intermediate
301 
302   Notes:
303   This may be used to bypass a call to `MatCreateSubMatrices()` and to `MatConvert()` for `MATSBAIJ` matrices.
304 
305   If a function is composed with DMCreateNeumannOverlap_C implementation is available in the `DM` attached to the Pmat, or the Amat, or the `PC`, the flag is internally set to `PETSC_TRUE`. Its default value is otherwise `PETSC_FALSE`.
306 
307 .seealso: [](ch_ksp), `PCHPDDM`, `PCHPDDMSetAuxiliaryMat()`
308 @*/
309 PetscErrorCode PCHPDDMHasNeumannMat(PC pc, PetscBool has)
310 {
311   PetscFunctionBegin;
312   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
313   PetscTryMethod(pc, "PCHPDDMHasNeumannMat_C", (PC, PetscBool), (pc, has));
314   PetscFunctionReturn(PETSC_SUCCESS);
315 }
316 
317 static PetscErrorCode PCHPDDMSetRHSMat_HPDDM(PC pc, Mat B)
318 {
319   PC_HPDDM *data = (PC_HPDDM *)pc->data;
320 
321   PetscFunctionBegin;
322   PetscCall(PetscObjectReference((PetscObject)B));
323   PetscCall(MatDestroy(&data->B));
324   data->B = B;
325   PetscFunctionReturn(PETSC_SUCCESS);
326 }
327 
328 /*@
329   PCHPDDMSetRHSMat - Sets the right-hand side matrix used by `PCHPDDM` for the concurrent GenEO problems at the finest level.
330 
331   Input Parameters:
332 + pc - preconditioner context
333 - B  - right-hand side sequential matrix
334 
335   Level: advanced
336 
337   Note:
338   Must be used in conjunction with `PCHPDDMSetAuxiliaryMat`(N), so that Nv = lambda Bv is solved using `EPSSetOperators`(N, B).
339   It is assumed that N and `B` are provided using the same numbering. This provides a means to try more advanced methods such as GenEO-II or H-GenEO.
340 
341 .seealso: [](ch_ksp), `PCHPDDMSetAuxiliaryMat()`, `PCHPDDM`
342 @*/
343 PetscErrorCode PCHPDDMSetRHSMat(PC pc, Mat B)
344 {
345   PetscFunctionBegin;
346   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
347   if (B) {
348     PetscValidHeaderSpecific(B, MAT_CLASSID, 2);
349     PetscTryMethod(pc, "PCHPDDMSetRHSMat_C", (PC, Mat), (pc, B));
350   }
351   PetscFunctionReturn(PETSC_SUCCESS);
352 }
353 
354 static PetscErrorCode PCSetFromOptions_HPDDM(PC pc, PetscOptionItems PetscOptionsObject)
355 {
356   PC_HPDDM                   *data   = (PC_HPDDM *)pc->data;
357   PC_HPDDM_Level            **levels = data->levels;
358   char                        prefix[256];
359   int                         i = 1;
360   PetscMPIInt                 size, previous;
361   PetscInt                    n, overlap = 1;
362   PCHPDDMCoarseCorrectionType type;
363   PetscBool                   flg = PETSC_TRUE, set;
364 
365   PetscFunctionBegin;
366   if (!data->levels) {
367     PetscCall(PetscCalloc1(PETSC_PCHPDDM_MAXLEVELS, &levels));
368     data->levels = levels;
369   }
370   PetscOptionsHeadBegin(PetscOptionsObject, "PCHPDDM options");
371   PetscCall(PetscOptionsBoundedInt("-pc_hpddm_harmonic_overlap", "Overlap prior to computing local harmonic extensions", "PCHPDDM", overlap, &overlap, &set, 1));
372   if (!set) overlap = -1;
373   PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)pc), &size));
374   previous = size;
375   while (i < PETSC_PCHPDDM_MAXLEVELS) {
376     PetscInt p = 1;
377 
378     if (!data->levels[i - 1]) PetscCall(PetscNew(data->levels + i - 1));
379     data->levels[i - 1]->parent = data;
380     /* if the previous level has a single process, it is not possible to coarsen further */
381     if (previous == 1 || !flg) break;
382     data->levels[i - 1]->nu        = 0;
383     data->levels[i - 1]->threshold = -1.0;
384     PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "-pc_hpddm_levels_%d_eps_nev", i));
385     PetscCall(PetscOptionsBoundedInt(prefix, "Local number of deflation vectors computed by SLEPc", "EPSSetDimensions", data->levels[i - 1]->nu, &data->levels[i - 1]->nu, nullptr, 0));
386     PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "-pc_hpddm_levels_%d_eps_threshold", i));
387     PetscCall(PetscOptionsReal(prefix, "Local threshold for selecting deflation vectors returned by SLEPc", "PCHPDDM", data->levels[i - 1]->threshold, &data->levels[i - 1]->threshold, nullptr));
388     if (i == 1) {
389       PetscCheck(overlap == -1 || PetscAbsReal(data->levels[i - 1]->threshold + static_cast<PetscReal>(1.0)) < PETSC_MACHINE_EPSILON, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cannot supply both -pc_hpddm_levels_1_eps_threshold and -pc_hpddm_harmonic_overlap");
390       if (overlap != -1) {
391         PetscInt nsv = 0;
392 
393         PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "-pc_hpddm_levels_%d_svd_nsv", i));
394         PetscCall(PetscOptionsBoundedInt(prefix, "Local number of deflation vectors computed by SLEPc", "SVDSetDimensions", nsv, &nsv, nullptr, 0));
395         PetscCheck(bool(data->levels[0]->nu) != bool(nsv), PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cannot supply %s -pc_hpddm_levels_1_eps_nev %s -pc_hpddm_levels_1_svd_nsv", nsv ? "both" : "neither", nsv ? "and" : "nor");
396         if (nsv) {
397           data->levels[0]->nu = nsv;
398           PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "-pc_hpddm_levels_%d_svd_relative_threshold", i));
399         } else PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "-pc_hpddm_levels_%d_eps_relative_threshold", i));
400         PetscCall(PetscOptionsReal(prefix, "Local relative threshold for selecting deflation vectors returned by SLEPc", "PCHPDDM", data->levels[0]->threshold, &data->levels[0]->threshold, nullptr));
401       }
402       PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "-pc_hpddm_levels_1_st_share_sub_ksp"));
403       PetscCall(PetscOptionsBool(prefix, "Shared KSP between SLEPc ST and the fine-level subdomain solver", "PCHPDDMSetSTShareSubKSP", PETSC_FALSE, &data->share, nullptr));
404     }
405     /* if there is no prescribed coarsening, just break out of the loop */
406     if (data->levels[i - 1]->threshold <= PetscReal() && data->levels[i - 1]->nu <= 0 && !(data->deflation && i == 1)) break;
407     else {
408       ++i;
409       PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "-pc_hpddm_levels_%d_eps_nev", i));
410       PetscCall(PetscOptionsHasName(PetscOptionsObject->options, PetscOptionsObject->prefix, prefix, &flg));
411       if (!flg) {
412         PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "-pc_hpddm_levels_%d_eps_threshold", i));
413         PetscCall(PetscOptionsHasName(PetscOptionsObject->options, PetscOptionsObject->prefix, prefix, &flg));
414       }
415       if (flg) {
416         /* if there are coarsening options for the next level, then register it  */
417         /* otherwise, don't to avoid having both options levels_N_p and coarse_p */
418         PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "-pc_hpddm_levels_%d_p", i));
419         PetscCall(PetscOptionsRangeInt(prefix, "Number of processes used to assemble the coarse operator at this level", "PCHPDDM", p, &p, &flg, 1, PetscMax(1, previous / 2)));
420         previous = p;
421       }
422     }
423   }
424   data->N = i;
425   n       = 1;
426   if (i > 1) {
427     PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "-pc_hpddm_coarse_p"));
428     PetscCall(PetscOptionsRangeInt(prefix, "Number of processes used to assemble the coarsest operator", "PCHPDDM", n, &n, nullptr, 1, PetscMax(1, previous / 2)));
429 #if PetscDefined(HAVE_MUMPS)
430     PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "pc_hpddm_coarse_"));
431     PetscCall(PetscOptionsHasName(nullptr, prefix, "-mat_mumps_use_omp_threads", &flg));
432     if (flg) {
433       char type[64]; /* same size as in src/ksp/pc/impls/factor/factimpl.c */
434 
435       PetscCall(PetscStrncpy(type, n > 1 && PetscDefined(HAVE_MUMPS) ? MATSOLVERMUMPS : MATSOLVERPETSC, sizeof(type))); /* default solver for a MatMPIAIJ or a MatSeqAIJ */
436       PetscCall(PetscOptionsGetString(nullptr, prefix, "-pc_factor_mat_solver_type", type, sizeof(type), nullptr));
437       PetscCall(PetscStrcmp(type, MATSOLVERMUMPS, &flg));
438       PetscCheck(flg, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "-%smat_mumps_use_omp_threads and -%spc_factor_mat_solver_type != %s", prefix, prefix, MATSOLVERMUMPS);
439       size = n;
440       n    = -1;
441       PetscCall(PetscOptionsGetInt(nullptr, prefix, "-mat_mumps_use_omp_threads", &n, nullptr));
442       PetscCheck(n >= 1, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Need to specify a positive integer for -%smat_mumps_use_omp_threads", prefix);
443       PetscCheck(n * size <= previous, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "%d MPI process%s x %d OpenMP thread%s greater than %d available MPI process%s for the coarsest operator", (int)size, size > 1 ? "es" : "", (int)n, n > 1 ? "s" : "", (int)previous, previous > 1 ? "es" : "");
444     }
445 #endif
446     PetscCall(PetscOptionsEnum("-pc_hpddm_coarse_correction", "Type of coarse correction applied each iteration", "PCHPDDMSetCoarseCorrectionType", PCHPDDMCoarseCorrectionTypes, (PetscEnum)data->correction, (PetscEnum *)&type, &flg));
447     if (flg) PetscCall(PCHPDDMSetCoarseCorrectionType(pc, type));
448     PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "-pc_hpddm_has_neumann"));
449     PetscCall(PetscOptionsBool(prefix, "Is the auxiliary Mat the local Neumann matrix?", "PCHPDDMHasNeumannMat", PetscBool3ToBool(data->Neumann), &flg, &set));
450     if (set) data->Neumann = PetscBoolToBool3(flg);
451     data->log_separate = PETSC_FALSE;
452     if (PetscDefined(USE_LOG)) {
453       PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "-pc_hpddm_log_separate"));
454       PetscCall(PetscOptionsBool(prefix, "Log events level by level instead of inside PCSetUp()/KSPSolve()", nullptr, data->log_separate, &data->log_separate, nullptr));
455     }
456   }
457   PetscOptionsHeadEnd();
458   while (i < PETSC_PCHPDDM_MAXLEVELS && data->levels[i]) PetscCall(PetscFree(data->levels[i++]));
459   PetscFunctionReturn(PETSC_SUCCESS);
460 }
461 
462 static PetscErrorCode PCApply_HPDDM(PC pc, Vec x, Vec y)
463 {
464   PC_HPDDM *data = (PC_HPDDM *)pc->data;
465 
466   PetscFunctionBegin;
467   PetscCall(PetscCitationsRegister(HPDDMCitation, &HPDDMCite));
468   PetscCheck(data->levels[0]->ksp, PETSC_COMM_SELF, PETSC_ERR_PLIB, "No KSP attached to PCHPDDM");
469   if (data->log_separate) PetscCall(PetscLogEventBegin(PC_HPDDM_Solve[0], data->levels[0]->ksp, nullptr, nullptr, nullptr)); /* coarser-level events are directly triggered in HPDDM */
470   PetscCall(KSPSolve(data->levels[0]->ksp, x, y));
471   if (data->log_separate) PetscCall(PetscLogEventEnd(PC_HPDDM_Solve[0], data->levels[0]->ksp, nullptr, nullptr, nullptr));
472   PetscFunctionReturn(PETSC_SUCCESS);
473 }
474 
475 static PetscErrorCode PCMatApply_HPDDM(PC pc, Mat X, Mat Y)
476 {
477   PC_HPDDM *data = (PC_HPDDM *)pc->data;
478 
479   PetscFunctionBegin;
480   PetscCall(PetscCitationsRegister(HPDDMCitation, &HPDDMCite));
481   PetscCheck(data->levels[0]->ksp, PETSC_COMM_SELF, PETSC_ERR_PLIB, "No KSP attached to PCHPDDM");
482   PetscCall(KSPMatSolve(data->levels[0]->ksp, X, Y));
483   PetscFunctionReturn(PETSC_SUCCESS);
484 }
485 
486 /*@
487   PCHPDDMGetComplexities - Computes the grid and operator complexities.
488 
489   Collective
490 
491   Input Parameter:
492 . pc - preconditioner context
493 
494   Output Parameters:
495 + gc - grid complexity $ \sum_i m_i / m_1 $
496 - oc - operator complexity $ \sum_i nnz_i / nnz_1 $
497 
498   Level: advanced
499 
500 .seealso: [](ch_ksp), `PCMGGetGridComplexity()`, `PCHPDDM`, `PCHYPRE`, `PCGAMG`
501 @*/
502 PetscErrorCode PCHPDDMGetComplexities(PC pc, PetscReal *gc, PetscReal *oc)
503 {
504   PC_HPDDM      *data = (PC_HPDDM *)pc->data;
505   MatInfo        info;
506   PetscLogDouble accumulate[2]{}, nnz1 = 1.0, m1 = 1.0;
507 
508   PetscFunctionBegin;
509   if (gc) {
510     PetscAssertPointer(gc, 2);
511     *gc = 0;
512   }
513   if (oc) {
514     PetscAssertPointer(oc, 3);
515     *oc = 0;
516   }
517   for (PetscInt n = 0; n < data->N; ++n) {
518     if (data->levels[n]->ksp) {
519       Mat       P, A = nullptr;
520       PetscInt  m;
521       PetscBool flg = PETSC_FALSE;
522 
523       PetscCall(KSPGetOperators(data->levels[n]->ksp, nullptr, &P));
524       PetscCall(MatGetSize(P, &m, nullptr));
525       accumulate[0] += m;
526       if (n == 0) {
527         PetscCall(PetscObjectTypeCompareAny((PetscObject)P, &flg, MATNORMAL, MATNORMALHERMITIAN, ""));
528         if (flg) {
529           PetscCall(MatConvert(P, MATAIJ, MAT_INITIAL_MATRIX, &A));
530           P = A;
531         } else {
532           PetscCall(PetscObjectTypeCompare((PetscObject)P, MATSCHURCOMPLEMENT, &flg));
533           PetscCall(PetscObjectReference((PetscObject)P));
534         }
535       }
536       if (!A && flg) accumulate[1] += m * m; /* assumption that a MATSCHURCOMPLEMENT is dense if stored explicitly */
537       else if (P->ops->getinfo) {
538         PetscCall(MatGetInfo(P, MAT_GLOBAL_SUM, &info));
539         accumulate[1] += info.nz_used;
540       }
541       if (n == 0) {
542         m1 = m;
543         if (!A && flg) nnz1 = m * m;
544         else if (P->ops->getinfo) nnz1 = info.nz_used;
545         PetscCall(MatDestroy(&P));
546       }
547     }
548   }
549   /* only process #0 has access to the full hierarchy by construction, so broadcast to ensure consistent outputs */
550   PetscCallMPI(MPI_Bcast(accumulate, 2, MPIU_PETSCLOGDOUBLE, 0, PetscObjectComm((PetscObject)pc)));
551   if (gc) *gc = static_cast<PetscReal>(accumulate[0] / m1);
552   if (oc) *oc = static_cast<PetscReal>(accumulate[1] / nnz1);
553   PetscFunctionReturn(PETSC_SUCCESS);
554 }
555 
556 static PetscErrorCode PCView_HPDDM(PC pc, PetscViewer viewer)
557 {
558   PC_HPDDM         *data = (PC_HPDDM *)pc->data;
559   PetscViewer       subviewer;
560   PetscViewerFormat format;
561   PetscSubcomm      subcomm;
562   PetscReal         oc, gc;
563   PetscInt          tabs;
564   PetscMPIInt       size, color, rank;
565   PetscBool         flg;
566   const char       *name;
567 
568   PetscFunctionBegin;
569   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &flg));
570   if (flg) {
571     PetscCall(PetscViewerASCIIPrintf(viewer, "level%s: %" PetscInt_FMT "\n", data->N > 1 ? "s" : "", data->N));
572     PetscCall(PCHPDDMGetComplexities(pc, &gc, &oc));
573     if (data->N > 1) {
574       if (!data->deflation) {
575         PetscCall(PetscViewerASCIIPrintf(viewer, "Neumann matrix attached? %s\n", PetscBools[PetscBool3ToBool(data->Neumann)]));
576         PetscCall(PetscViewerASCIIPrintf(viewer, "shared subdomain KSP between SLEPc and PETSc? %s\n", PetscBools[data->share]));
577       } else PetscCall(PetscViewerASCIIPrintf(viewer, "user-supplied deflation matrix\n"));
578       PetscCall(PetscViewerASCIIPrintf(viewer, "coarse correction: %s\n", PCHPDDMCoarseCorrectionTypes[data->correction]));
579       PetscCall(PetscViewerASCIIPrintf(viewer, "on process #0, value%s (+ threshold%s if available) for selecting deflation vectors:", data->N > 2 ? "s" : "", data->N > 2 ? "s" : ""));
580       PetscCall(PetscViewerASCIIGetTab(viewer, &tabs));
581       PetscCall(PetscViewerASCIISetTab(viewer, 0));
582       for (PetscInt i = 1; i < data->N; ++i) {
583         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, data->levels[i - 1]->nu));
584         if (data->levels[i - 1]->threshold > static_cast<PetscReal>(-0.1)) PetscCall(PetscViewerASCIIPrintf(viewer, " (%g)", (double)data->levels[i - 1]->threshold));
585       }
586       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
587       PetscCall(PetscViewerASCIISetTab(viewer, tabs));
588     }
589     PetscCall(PetscViewerASCIIPrintf(viewer, "grid and operator complexities: %g %g\n", (double)gc, (double)oc));
590     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)pc), &size));
591     if (data->levels[0]->ksp) {
592       PetscCall(KSPView(data->levels[0]->ksp, viewer));
593       if (data->levels[0]->pc) PetscCall(PCView(data->levels[0]->pc, viewer));
594       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)pc), &rank));
595       for (PetscInt i = 1; i < data->N; ++i) {
596         if (data->levels[i]->ksp) color = 1;
597         else color = 0;
598         PetscCall(PetscSubcommCreate(PetscObjectComm((PetscObject)pc), &subcomm));
599         PetscCall(PetscSubcommSetNumber(subcomm, PetscMin(size, 2)));
600         PetscCall(PetscSubcommSetTypeGeneral(subcomm, color, rank));
601         PetscCall(PetscViewerASCIIPushTab(viewer));
602         PetscCall(PetscViewerGetSubViewer(viewer, PetscSubcommChild(subcomm), &subviewer));
603         if (color == 1) {
604           PetscCall(KSPView(data->levels[i]->ksp, subviewer));
605           if (data->levels[i]->pc) PetscCall(PCView(data->levels[i]->pc, subviewer));
606           PetscCall(PetscViewerFlush(subviewer));
607         }
608         PetscCall(PetscViewerRestoreSubViewer(viewer, PetscSubcommChild(subcomm), &subviewer));
609         PetscCall(PetscViewerASCIIPopTab(viewer));
610         PetscCall(PetscSubcommDestroy(&subcomm));
611       }
612     }
613     PetscCall(PetscViewerGetFormat(viewer, &format));
614     if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
615       PetscCall(PetscViewerFileGetName(viewer, &name));
616       if (name) {
617         Mat             aux[2];
618         IS              is;
619         const PetscInt *indices;
620         PetscInt        m, n, sizes[5] = {pc->mat->rmap->n, pc->mat->cmap->n, pc->mat->rmap->N, pc->mat->cmap->N, 0};
621         char           *tmp;
622         std::string     prefix, suffix;
623         size_t          pos;
624 
625         PetscCall(PetscStrstr(name, ".", &tmp));
626         if (tmp) {
627           pos    = std::distance(const_cast<char *>(name), tmp);
628           prefix = std::string(name, pos);
629           suffix = std::string(name + pos + 1);
630         } else prefix = name;
631         if (data->aux) {
632           PetscCall(MatGetSize(data->aux, &m, &n));
633           PetscCall(MatCreate(PetscObjectComm((PetscObject)pc), aux));
634           PetscCall(MatSetSizes(aux[0], m, n, PETSC_DETERMINE, PETSC_DETERMINE));
635           PetscCall(PetscObjectBaseTypeCompare((PetscObject)data->aux, MATSEQAIJ, &flg));
636           if (flg) PetscCall(MatSetType(aux[0], MATMPIAIJ));
637           else {
638             PetscCall(PetscObjectBaseTypeCompare((PetscObject)data->aux, MATSEQBAIJ, &flg));
639             if (flg) PetscCall(MatSetType(aux[0], MATMPIBAIJ));
640             else {
641               PetscCall(PetscObjectBaseTypeCompare((PetscObject)data->aux, MATSEQSBAIJ, &flg));
642               PetscCheck(flg, PetscObjectComm((PetscObject)pc), PETSC_ERR_SUP, "MatType of auxiliary Mat (%s) is not any of the following: MATSEQAIJ, MATSEQBAIJ, or MATSEQSBAIJ", ((PetscObject)data->aux)->type_name);
643               PetscCall(MatSetType(aux[0], MATMPISBAIJ));
644             }
645           }
646           PetscCall(MatSetBlockSizesFromMats(aux[0], data->aux, data->aux));
647           PetscCall(MatAssemblyBegin(aux[0], MAT_FINAL_ASSEMBLY));
648           PetscCall(MatAssemblyEnd(aux[0], MAT_FINAL_ASSEMBLY));
649           PetscCall(MatGetDiagonalBlock(aux[0], aux + 1));
650           PetscCall(MatCopy(data->aux, aux[1], DIFFERENT_NONZERO_PATTERN));
651           PetscCall(PetscViewerBinaryOpen(PetscObjectComm((PetscObject)pc), std::string(prefix + "_aux_" + std::to_string(size) + (tmp ? ("." + suffix) : "")).c_str(), FILE_MODE_WRITE, &subviewer));
652           PetscCall(MatView(aux[0], subviewer));
653           PetscCall(PetscViewerDestroy(&subviewer));
654           PetscCall(MatDestroy(aux));
655         }
656         if (data->is) {
657           PetscCall(ISGetIndices(data->is, &indices));
658           PetscCall(ISGetSize(data->is, sizes + 4));
659           PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)pc), sizes[4], indices, PETSC_USE_POINTER, &is));
660           PetscCall(PetscViewerBinaryOpen(PetscObjectComm((PetscObject)pc), std::string(prefix + "_is_" + std::to_string(size) + (tmp ? ("." + suffix) : "")).c_str(), FILE_MODE_WRITE, &subviewer));
661           PetscCall(ISView(is, subviewer));
662           PetscCall(PetscViewerDestroy(&subviewer));
663           PetscCall(ISDestroy(&is));
664           PetscCall(ISRestoreIndices(data->is, &indices));
665         }
666         PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)pc), PETSC_STATIC_ARRAY_LENGTH(sizes), sizes, PETSC_USE_POINTER, &is));
667         PetscCall(PetscViewerBinaryOpen(PetscObjectComm((PetscObject)pc), std::string(prefix + "_sizes_" + std::to_string(size) + (tmp ? ("." + suffix) : "")).c_str(), FILE_MODE_WRITE, &subviewer));
668         PetscCall(ISView(is, subviewer));
669         PetscCall(PetscViewerDestroy(&subviewer));
670         PetscCall(ISDestroy(&is));
671       }
672     }
673   }
674   PetscFunctionReturn(PETSC_SUCCESS);
675 }
676 
677 static PetscErrorCode PCPreSolve_HPDDM(PC pc, KSP ksp, Vec, Vec)
678 {
679   PC_HPDDM *data = (PC_HPDDM *)pc->data;
680   Mat       A;
681   PetscBool flg;
682 
683   PetscFunctionBegin;
684   if (ksp) {
685     PetscCall(PetscObjectTypeCompare((PetscObject)ksp, KSPLSQR, &flg));
686     if (flg && !data->normal) {
687       PetscCall(KSPGetOperators(ksp, &A, nullptr));
688       PetscCall(MatCreateVecs(A, nullptr, &data->normal)); /* temporary Vec used in PCApply_HPDDMShell() for coarse grid corrections */
689     } else if (!flg) {
690       PetscCall(PetscObjectTypeCompareAny((PetscObject)ksp, &flg, KSPCG, KSPGROPPCG, KSPPIPECG, KSPPIPECGRR, KSPPIPELCG, KSPPIPEPRCG, KSPPIPECG2, KSPSTCG, KSPFCG, KSPPIPEFCG, KSPMINRES, KSPNASH, KSPSYMMLQ, ""));
691       if (!flg) {
692         PetscCall(PetscObjectTypeCompare((PetscObject)ksp, KSPHPDDM, &flg));
693         if (flg) {
694           KSPHPDDMType type;
695 
696           PetscCall(KSPHPDDMGetType(ksp, &type));
697           flg = (type == KSP_HPDDM_TYPE_CG || type == KSP_HPDDM_TYPE_BCG || type == KSP_HPDDM_TYPE_BFBCG ? PETSC_TRUE : PETSC_FALSE);
698         }
699       }
700     }
701     if (flg) {
702       if (data->correction == PC_HPDDM_COARSE_CORRECTION_DEFLATED) {
703         PetscCall(PetscOptionsHasName(((PetscObject)pc)->options, ((PetscObject)pc)->prefix, "-pc_hpddm_coarse_correction", &flg));
704         PetscCheck(flg, PetscObjectComm((PetscObject)pc), PETSC_ERR_ARG_INCOMP, "PCHPDDMCoarseCorrectionType %s is known to be not symmetric, but KSPType %s requires a symmetric PC, if you insist on using this configuration, use the additional option -%spc_hpddm_coarse_correction %s, or alternatively, switch to a symmetric PCHPDDMCoarseCorrectionType such as %s",
705                    PCHPDDMCoarseCorrectionTypes[data->correction], ((PetscObject)ksp)->type_name, ((PetscObject)pc)->prefix ? ((PetscObject)pc)->prefix : "", PCHPDDMCoarseCorrectionTypes[data->correction], PCHPDDMCoarseCorrectionTypes[PC_HPDDM_COARSE_CORRECTION_BALANCED]);
706       }
707       for (PetscInt n = 0; n < data->N; ++n) {
708         if (data->levels[n]->pc) {
709           PetscCall(PetscObjectTypeCompare((PetscObject)data->levels[n]->pc, PCASM, &flg));
710           if (flg) {
711             PCASMType type;
712 
713             PetscCall(PCASMGetType(data->levels[n]->pc, &type));
714             if (type == PC_ASM_RESTRICT || type == PC_ASM_INTERPOLATE) {
715               PetscCall(PetscOptionsHasName(((PetscObject)data->levels[n]->pc)->options, ((PetscObject)data->levels[n]->pc)->prefix, "-pc_asm_type", &flg));
716               PetscCheck(flg, PetscObjectComm((PetscObject)data->levels[n]->pc), PETSC_ERR_ARG_INCOMP, "PCASMType %s is known to be not symmetric, but KSPType %s requires a symmetric PC, if you insist on using this configuration, use the additional option -%spc_asm_type %s, or alternatively, switch to a symmetric PCASMType such as %s", PCASMTypes[type],
717                          ((PetscObject)ksp)->type_name, ((PetscObject)data->levels[n]->pc)->prefix, PCASMTypes[type], PCASMTypes[PC_ASM_BASIC]);
718             }
719           }
720         }
721       }
722     }
723   }
724   PetscFunctionReturn(PETSC_SUCCESS);
725 }
726 
727 static PetscErrorCode PCSetUp_HPDDMShell(PC pc)
728 {
729   PC_HPDDM_Level *ctx;
730   Mat             A, P;
731   Vec             x;
732   const char     *pcpre;
733 
734   PetscFunctionBegin;
735   PetscCall(PCShellGetContext(pc, &ctx));
736   PetscCall(KSPGetOptionsPrefix(ctx->ksp, &pcpre));
737   PetscCall(KSPGetOperators(ctx->ksp, &A, &P));
738   /* smoother */
739   PetscCall(PCSetOptionsPrefix(ctx->pc, pcpre));
740   PetscCall(PCSetOperators(ctx->pc, A, P));
741   if (!ctx->v[0]) {
742     PetscCall(VecDuplicateVecs(ctx->D, 1, &ctx->v[0]));
743     if (!std::is_same<PetscScalar, PetscReal>::value) PetscCall(VecDestroy(&ctx->D));
744     PetscCall(MatCreateVecs(A, &x, nullptr));
745     PetscCall(VecDuplicateVecs(x, 2, &ctx->v[1]));
746     PetscCall(VecDestroy(&x));
747   }
748   std::fill_n(ctx->V, 3, nullptr);
749   PetscFunctionReturn(PETSC_SUCCESS);
750 }
751 
752 template <class Type, typename std::enable_if<std::is_same<Type, Vec>::value>::type * = nullptr>
753 static inline PetscErrorCode PCHPDDMDeflate_Private(PC pc, Type x, Type y)
754 {
755   PC_HPDDM_Level *ctx;
756   PetscScalar    *out;
757 
758   PetscFunctionBegin;
759   PetscCall(PCShellGetContext(pc, &ctx));
760   /* going from PETSc to HPDDM numbering */
761   PetscCall(VecScatterBegin(ctx->scatter, x, ctx->v[0][0], INSERT_VALUES, SCATTER_FORWARD));
762   PetscCall(VecScatterEnd(ctx->scatter, x, ctx->v[0][0], INSERT_VALUES, SCATTER_FORWARD));
763   PetscCall(VecGetArrayWrite(ctx->v[0][0], &out));
764   ctx->P->deflation<false>(nullptr, out, 1); /* y = Q x */
765   PetscCall(VecRestoreArrayWrite(ctx->v[0][0], &out));
766   /* going from HPDDM to PETSc numbering */
767   PetscCall(VecScatterBegin(ctx->scatter, ctx->v[0][0], y, INSERT_VALUES, SCATTER_REVERSE));
768   PetscCall(VecScatterEnd(ctx->scatter, ctx->v[0][0], y, INSERT_VALUES, SCATTER_REVERSE));
769   PetscFunctionReturn(PETSC_SUCCESS);
770 }
771 
772 template <class Type, typename std::enable_if<std::is_same<Type, Mat>::value>::type * = nullptr>
773 static inline PetscErrorCode PCHPDDMDeflate_Private(PC pc, Type X, Type Y)
774 {
775   PC_HPDDM_Level *ctx;
776   Vec             vX, vY, vC;
777   PetscScalar    *out;
778   PetscInt        N;
779 
780   PetscFunctionBegin;
781   PetscCall(PCShellGetContext(pc, &ctx));
782   PetscCall(MatGetSize(X, nullptr, &N));
783   /* going from PETSc to HPDDM numbering */
784   for (PetscInt i = 0; i < N; ++i) {
785     PetscCall(MatDenseGetColumnVecRead(X, i, &vX));
786     PetscCall(MatDenseGetColumnVecWrite(ctx->V[0], i, &vC));
787     PetscCall(VecScatterBegin(ctx->scatter, vX, vC, INSERT_VALUES, SCATTER_FORWARD));
788     PetscCall(VecScatterEnd(ctx->scatter, vX, vC, INSERT_VALUES, SCATTER_FORWARD));
789     PetscCall(MatDenseRestoreColumnVecWrite(ctx->V[0], i, &vC));
790     PetscCall(MatDenseRestoreColumnVecRead(X, i, &vX));
791   }
792   PetscCall(MatDenseGetArrayWrite(ctx->V[0], &out));
793   ctx->P->deflation<false>(nullptr, out, N); /* Y = Q X */
794   PetscCall(MatDenseRestoreArrayWrite(ctx->V[0], &out));
795   /* going from HPDDM to PETSc numbering */
796   for (PetscInt i = 0; i < N; ++i) {
797     PetscCall(MatDenseGetColumnVecRead(ctx->V[0], i, &vC));
798     PetscCall(MatDenseGetColumnVecWrite(Y, i, &vY));
799     PetscCall(VecScatterBegin(ctx->scatter, vC, vY, INSERT_VALUES, SCATTER_REVERSE));
800     PetscCall(VecScatterEnd(ctx->scatter, vC, vY, INSERT_VALUES, SCATTER_REVERSE));
801     PetscCall(MatDenseRestoreColumnVecWrite(Y, i, &vY));
802     PetscCall(MatDenseRestoreColumnVecRead(ctx->V[0], i, &vC));
803   }
804   PetscFunctionReturn(PETSC_SUCCESS);
805 }
806 
807 /*
808      PCApply_HPDDMShell - Applies a (2) deflated, (1) additive, (3) balanced, or (4) no coarse correction. In what follows, E = Z Pmat Z^T and Q = Z^T E^-1 Z.
809 
810 .vb
811    (1) y =                Pmat^-1              x + Q x,
812    (2) y =                Pmat^-1 (I - Amat Q) x + Q x (default),
813    (3) y = (I - Q Amat^T) Pmat^-1 (I - Amat Q) x + Q x,
814    (4) y =                Pmat^-1              x      .
815 .ve
816 
817    Input Parameters:
818 +     pc - preconditioner context
819 -     x - input vector
820 
821    Output Parameter:
822 .     y - output vector
823 
824    Notes:
825      The options of Pmat^1 = pc(Pmat) are prefixed by -pc_hpddm_levels_1_pc_. Z is a tall-and-skiny matrix assembled by HPDDM. The number of processes on which (Z Pmat Z^T) is aggregated is set via -pc_hpddm_coarse_p.
826      The options of (Z Pmat Z^T)^-1 = ksp(Z Pmat Z^T) are prefixed by -pc_hpddm_coarse_ (`KSPPREONLY` and `PCCHOLESKY` by default), unless a multilevel correction is turned on, in which case, this function is called recursively at each level except the coarsest one.
827      (1) and (2) visit the "next" level (in terms of coarsening) once per application, while (3) visits it twice, so it is asymptotically twice costlier. (2) is not symmetric even if both Amat and Pmat are symmetric.
828 
829    Level: advanced
830 
831    Developer Note:
832    Since this is not an actual manual page the material below should be moved to an appropriate manual page with the appropriate context, i.e. explaining when it is used and how
833    to trigger it. Likely the manual page is `PCHPDDM`
834 
835 .seealso: [](ch_ksp), `PCHPDDM`, `PCHPDDMCoarseCorrectionType`
836 */
837 static PetscErrorCode PCApply_HPDDMShell(PC pc, Vec x, Vec y)
838 {
839   PC_HPDDM_Level *ctx;
840   Mat             A;
841 
842   PetscFunctionBegin;
843   PetscCall(PCShellGetContext(pc, &ctx));
844   PetscCheck(ctx->P, PETSC_COMM_SELF, PETSC_ERR_PLIB, "PCSHELL from PCHPDDM called with no HPDDM object");
845   PetscCall(KSPGetOperators(ctx->ksp, &A, nullptr));
846   if (ctx->parent->correction == PC_HPDDM_COARSE_CORRECTION_NONE) PetscCall(PCApply(ctx->pc, x, y)); /* y = M^-1 x */
847   else {
848     PetscCall(PCHPDDMDeflate_Private(pc, x, y)); /* y = Q x */
849     if (ctx->parent->correction == PC_HPDDM_COARSE_CORRECTION_DEFLATED || ctx->parent->correction == PC_HPDDM_COARSE_CORRECTION_BALANCED) {
850       if (!ctx->parent->normal || ctx != ctx->parent->levels[0]) PetscCall(MatMult(A, y, ctx->v[1][0])); /* y = A Q x     */
851       else { /* KSPLSQR and finest level */ PetscCall(MatMult(A, y, ctx->parent->normal));               /* y = A Q x     */
852         PetscCall(MatMultHermitianTranspose(A, ctx->parent->normal, ctx->v[1][0]));                      /* y = A^T A Q x */
853       }
854       PetscCall(VecWAXPY(ctx->v[1][1], -1.0, ctx->v[1][0], x)); /* y = (I - A Q) x                             */
855       PetscCall(PCApply(ctx->pc, ctx->v[1][1], ctx->v[1][0]));  /* y = M^-1 (I - A Q) x                        */
856       if (ctx->parent->correction == PC_HPDDM_COARSE_CORRECTION_BALANCED) {
857         if (!ctx->parent->normal || ctx != ctx->parent->levels[0]) PetscCall(MatMultHermitianTranspose(A, ctx->v[1][0], ctx->v[1][1])); /* z = A^T y */
858         else {
859           PetscCall(MatMult(A, ctx->v[1][0], ctx->parent->normal));
860           PetscCall(MatMultHermitianTranspose(A, ctx->parent->normal, ctx->v[1][1])); /* z = A^T A y           */
861         }
862         PetscCall(PCHPDDMDeflate_Private(pc, ctx->v[1][1], ctx->v[1][1]));
863         PetscCall(VecAXPBYPCZ(y, -1.0, 1.0, 1.0, ctx->v[1][1], ctx->v[1][0])); /* y = (I - Q A^T) y + Q x      */
864       } else PetscCall(VecAXPY(y, 1.0, ctx->v[1][0]));                         /* y = Q M^-1 (I - A Q) x + Q x */
865     } else {
866       PetscCheck(ctx->parent->correction == PC_HPDDM_COARSE_CORRECTION_ADDITIVE, PetscObjectComm((PetscObject)pc), PETSC_ERR_PLIB, "PCSHELL from PCHPDDM called with an unknown PCHPDDMCoarseCorrectionType %d", ctx->parent->correction);
867       PetscCall(PCApply(ctx->pc, x, ctx->v[1][0]));
868       PetscCall(VecAXPY(y, 1.0, ctx->v[1][0])); /* y = M^-1 x + Q x */
869     }
870   }
871   PetscFunctionReturn(PETSC_SUCCESS);
872 }
873 
874 /*
875      PCMatApply_HPDDMShell - Variant of PCApply_HPDDMShell() for blocks of vectors.
876 
877    Input Parameters:
878 +     pc - preconditioner context
879 -     X - block of input vectors
880 
881    Output Parameter:
882 .     Y - block of output vectors
883 
884    Level: advanced
885 
886 .seealso: [](ch_ksp), `PCHPDDM`, `PCApply_HPDDMShell()`, `PCHPDDMCoarseCorrectionType`
887 */
888 static PetscErrorCode PCMatApply_HPDDMShell(PC pc, Mat X, Mat Y)
889 {
890   PC_HPDDM_Level *ctx;
891   Mat             A, *ptr;
892   PetscContainer  container = nullptr;
893   PetscScalar    *array;
894   PetscInt        m, M, N, prev = 0;
895   PetscBool       reset = PETSC_FALSE;
896 
897   PetscFunctionBegin;
898   PetscCall(PCShellGetContext(pc, &ctx));
899   PetscCheck(ctx->P, PETSC_COMM_SELF, PETSC_ERR_PLIB, "PCSHELL from PCHPDDM called with no HPDDM object");
900   PetscCall(MatGetSize(X, nullptr, &N));
901   PetscCall(KSPGetOperators(ctx->ksp, &A, nullptr));
902   if (ctx->parent->correction == PC_HPDDM_COARSE_CORRECTION_NONE) PetscCall(PCMatApply(ctx->pc, X, Y));
903   else {
904     PetscCall(PetscObjectQuery((PetscObject)A, "_HPDDM_MatProduct", (PetscObject *)&container));
905     if (container) { /* MatProduct container already attached */
906       PetscCall(PetscContainerGetPointer(container, (void **)&ptr));
907       if (ptr[1] != ctx->V[2]) /* Mat has changed or may have been set first in KSPHPDDM */
908         for (m = 0; m < 2; ++m) {
909           PetscCall(MatDestroy(ctx->V + m + 1));
910           ctx->V[m + 1] = ptr[m];
911           PetscCall(PetscObjectReference((PetscObject)ctx->V[m + 1]));
912         }
913     }
914     if (ctx->V[1]) PetscCall(MatGetSize(ctx->V[1], nullptr, &prev));
915     if (N != prev || !ctx->V[0]) {
916       PetscCall(MatDestroy(ctx->V));
917       PetscCall(VecGetLocalSize(ctx->v[0][0], &m));
918       PetscCall(MatCreateDense(PetscObjectComm((PetscObject)pc), m, PETSC_DECIDE, PETSC_DECIDE, N, nullptr, ctx->V));
919       if (N != prev) {
920         PetscCall(MatDestroy(ctx->V + 1));
921         PetscCall(MatDestroy(ctx->V + 2));
922         PetscCall(MatGetLocalSize(X, &m, nullptr));
923         PetscCall(MatGetSize(X, &M, nullptr));
924         if (ctx->parent->correction != PC_HPDDM_COARSE_CORRECTION_BALANCED) PetscCall(MatDenseGetArrayWrite(ctx->V[0], &array));
925         else array = nullptr;
926         PetscCall(MatCreateDense(PetscObjectComm((PetscObject)pc), m, PETSC_DECIDE, M, N, array, ctx->V + 1));
927         if (ctx->parent->correction != PC_HPDDM_COARSE_CORRECTION_BALANCED) PetscCall(MatDenseRestoreArrayWrite(ctx->V[0], &array));
928         PetscCall(MatCreateDense(PetscObjectComm((PetscObject)pc), m, PETSC_DECIDE, M, N, nullptr, ctx->V + 2));
929         PetscCall(MatProductCreateWithMat(A, Y, nullptr, ctx->V[1]));
930         PetscCall(MatProductSetType(ctx->V[1], MATPRODUCT_AB));
931         PetscCall(MatProductSetFromOptions(ctx->V[1]));
932         PetscCall(MatProductSymbolic(ctx->V[1]));
933         if (!container) PetscCall(PetscObjectContainerCompose((PetscObject)A, "_HPDDM_MatProduct", ctx->V + 1, nullptr)); /* no MatProduct container attached, create one to be queried in KSPHPDDM or at the next call to PCMatApply() */
934         else PetscCall(PetscContainerSetPointer(container, ctx->V + 1));                                                  /* need to compose B and D from MatProductCreateWithMat(A, B, NULL, D), which are stored in the contiguous array ctx->V */
935       }
936       if (ctx->parent->correction == PC_HPDDM_COARSE_CORRECTION_BALANCED) {
937         PetscCall(MatProductCreateWithMat(A, ctx->V[1], nullptr, ctx->V[2]));
938         PetscCall(MatProductSetType(ctx->V[2], MATPRODUCT_AtB));
939         PetscCall(MatProductSetFromOptions(ctx->V[2]));
940         PetscCall(MatProductSymbolic(ctx->V[2]));
941       }
942       ctx->P->start(N);
943     }
944     if (N == prev || container) { /* when MatProduct container is attached, always need to MatProductReplaceMats() since KSPHPDDM may have replaced the Mat as well */
945       PetscCall(MatProductReplaceMats(nullptr, Y, nullptr, ctx->V[1]));
946       if (container && ctx->parent->correction != PC_HPDDM_COARSE_CORRECTION_BALANCED) {
947         PetscCall(MatDenseGetArrayWrite(ctx->V[0], &array));
948         PetscCall(MatDensePlaceArray(ctx->V[1], array));
949         PetscCall(MatDenseRestoreArrayWrite(ctx->V[0], &array));
950         reset = PETSC_TRUE;
951       }
952     }
953     PetscCall(PCHPDDMDeflate_Private(pc, X, Y));
954     if (ctx->parent->correction == PC_HPDDM_COARSE_CORRECTION_DEFLATED || ctx->parent->correction == PC_HPDDM_COARSE_CORRECTION_BALANCED) {
955       PetscCall(MatProductNumeric(ctx->V[1]));
956       PetscCall(MatCopy(ctx->V[1], ctx->V[2], SAME_NONZERO_PATTERN));
957       PetscCall(MatAXPY(ctx->V[2], -1.0, X, SAME_NONZERO_PATTERN));
958       PetscCall(PCMatApply(ctx->pc, ctx->V[2], ctx->V[1]));
959       if (ctx->parent->correction == PC_HPDDM_COARSE_CORRECTION_BALANCED) {
960         PetscCall(MatProductNumeric(ctx->V[2]));
961         PetscCall(PCHPDDMDeflate_Private(pc, ctx->V[2], ctx->V[2]));
962         PetscCall(MatAXPY(ctx->V[1], -1.0, ctx->V[2], SAME_NONZERO_PATTERN));
963       }
964       PetscCall(MatAXPY(Y, -1.0, ctx->V[1], SAME_NONZERO_PATTERN));
965     } else {
966       PetscCheck(ctx->parent->correction == PC_HPDDM_COARSE_CORRECTION_ADDITIVE, PetscObjectComm((PetscObject)pc), PETSC_ERR_PLIB, "PCSHELL from PCHPDDM called with an unknown PCHPDDMCoarseCorrectionType %d", ctx->parent->correction);
967       PetscCall(PCMatApply(ctx->pc, X, ctx->V[1]));
968       PetscCall(MatAXPY(Y, 1.0, ctx->V[1], SAME_NONZERO_PATTERN));
969     }
970     if (reset) PetscCall(MatDenseResetArray(ctx->V[1]));
971   }
972   PetscFunctionReturn(PETSC_SUCCESS);
973 }
974 
975 static PetscErrorCode PCDestroy_HPDDMShell(PC pc)
976 {
977   PC_HPDDM_Level *ctx;
978 
979   PetscFunctionBegin;
980   PetscCall(PCShellGetContext(pc, &ctx));
981   PetscCall(HPDDM::Schwarz<PetscScalar>::destroy(ctx, PETSC_TRUE));
982   PetscCall(VecDestroyVecs(1, &ctx->v[0]));
983   PetscCall(VecDestroyVecs(2, &ctx->v[1]));
984   PetscCall(PetscObjectCompose((PetscObject)ctx->pc->mat, "_HPDDM_MatProduct", nullptr));
985   PetscCall(MatDestroy(ctx->V));
986   PetscCall(MatDestroy(ctx->V + 1));
987   PetscCall(MatDestroy(ctx->V + 2));
988   PetscCall(VecDestroy(&ctx->D));
989   PetscCall(PetscSFDestroy(&ctx->scatter));
990   PetscCall(PCDestroy(&ctx->pc));
991   PetscFunctionReturn(PETSC_SUCCESS);
992 }
993 
994 template <class Type, bool T = false, typename std::enable_if<std::is_same<Type, Vec>::value>::type * = nullptr>
995 static inline PetscErrorCode PCApply_Schur_Private(std::tuple<KSP, IS, Vec[2]> *p, PC factor, Type x, Type y)
996 {
997   PetscFunctionBegin;
998   PetscCall(VecISCopy(std::get<2>(*p)[0], std::get<1>(*p), SCATTER_FORWARD, x));
999   if (!T) PetscCall(PCApply(factor, std::get<2>(*p)[0], std::get<2>(*p)[1]));
1000   else PetscCall(PCApplyTranspose(factor, std::get<2>(*p)[0], std::get<2>(*p)[1]));
1001   PetscCall(VecISCopy(std::get<2>(*p)[1], std::get<1>(*p), SCATTER_REVERSE, y));
1002   PetscFunctionReturn(PETSC_SUCCESS);
1003 }
1004 
1005 template <class Type, bool = false, typename std::enable_if<std::is_same<Type, Mat>::value>::type * = nullptr>
1006 static inline PetscErrorCode PCApply_Schur_Private(std::tuple<KSP, IS, Vec[2]> *p, PC factor, Type X, Type Y)
1007 {
1008   Mat B[2];
1009   Vec x, y;
1010 
1011   PetscFunctionBegin;
1012   PetscCall(MatCreateSeqDense(PETSC_COMM_SELF, factor->mat->rmap->n, X->cmap->n, nullptr, B));
1013   PetscCall(MatCreateSeqDense(PETSC_COMM_SELF, factor->mat->rmap->n, X->cmap->n, nullptr, B + 1));
1014   for (PetscInt i = 0; i < X->cmap->n; ++i) {
1015     PetscCall(MatDenseGetColumnVecRead(X, i, &x));
1016     PetscCall(MatDenseGetColumnVecWrite(B[0], i, &y));
1017     PetscCall(VecISCopy(y, std::get<1>(*p), SCATTER_FORWARD, x));
1018     PetscCall(MatDenseRestoreColumnVecWrite(B[0], i, &y));
1019     PetscCall(MatDenseRestoreColumnVecRead(X, i, &x));
1020   }
1021   PetscCall(PCMatApply(factor, B[0], B[1]));
1022   PetscCall(MatDestroy(B));
1023   for (PetscInt i = 0; i < X->cmap->n; ++i) {
1024     PetscCall(MatDenseGetColumnVecRead(B[1], i, &x));
1025     PetscCall(MatDenseGetColumnVecWrite(Y, i, &y));
1026     PetscCall(VecISCopy(x, std::get<1>(*p), SCATTER_REVERSE, y));
1027     PetscCall(MatDenseRestoreColumnVecWrite(Y, i, &y));
1028     PetscCall(MatDenseRestoreColumnVecRead(B[1], i, &x));
1029   }
1030   PetscCall(MatDestroy(B + 1));
1031   PetscFunctionReturn(PETSC_SUCCESS);
1032 }
1033 
1034 template <class Type = Vec, bool T = false>
1035 static PetscErrorCode PCApply_Schur(PC pc, Type x, Type y)
1036 {
1037   PC                           factor;
1038   Mat                          A;
1039   MatSolverType                type;
1040   PetscBool                    flg;
1041   std::tuple<KSP, IS, Vec[2]> *p;
1042 
1043   PetscFunctionBegin;
1044   PetscCall(PCShellGetContext(pc, &p));
1045   PetscCall(KSPGetPC(std::get<0>(*p), &factor));
1046   PetscCall(PCFactorGetMatSolverType(factor, &type));
1047   PetscCall(PCFactorGetMatrix(factor, &A));
1048   PetscCall(PetscStrcmp(type, MATSOLVERMUMPS, &flg));
1049   if (flg) {
1050     PetscCheck(PetscDefined(HAVE_MUMPS), PETSC_COMM_SELF, PETSC_ERR_PLIB, "Inconsistent MatSolverType");
1051 #if PetscDefined(HAVE_MUMPS)
1052     PetscCall(MatMumpsSetIcntl(A, 26, 0));
1053 #endif
1054   } else {
1055     PetscCall(PetscStrcmp(type, MATSOLVERMKL_PARDISO, &flg));
1056     PetscCheck(flg && PetscDefined(HAVE_MKL_PARDISO), PETSC_COMM_SELF, PETSC_ERR_PLIB, "Inconsistent MatSolverType");
1057     flg = PETSC_FALSE;
1058 #if PetscDefined(HAVE_MKL_PARDISO)
1059     PetscCall(MatMkl_PardisoSetCntl(A, 70, 1));
1060 #endif
1061   }
1062   PetscCall(PCApply_Schur_Private<Type, T>(p, factor, x, y));
1063   if (flg) {
1064 #if PetscDefined(HAVE_MUMPS)
1065     PetscCall(MatMumpsSetIcntl(A, 26, -1));
1066 #endif
1067   } else {
1068 #if PetscDefined(HAVE_MKL_PARDISO)
1069     PetscCall(MatMkl_PardisoSetCntl(A, 70, 0));
1070 #endif
1071   }
1072   PetscFunctionReturn(PETSC_SUCCESS);
1073 }
1074 
1075 static PetscErrorCode PCDestroy_Schur(PC pc)
1076 {
1077   std::tuple<KSP, IS, Vec[2]> *p;
1078 
1079   PetscFunctionBegin;
1080   PetscCall(PCShellGetContext(pc, &p));
1081   PetscCall(ISDestroy(&std::get<1>(*p)));
1082   PetscCall(VecDestroy(std::get<2>(*p)));
1083   PetscCall(VecDestroy(std::get<2>(*p) + 1));
1084   PetscCall(PetscFree(p));
1085   PetscFunctionReturn(PETSC_SUCCESS);
1086 }
1087 
1088 static PetscErrorCode PCHPDDMSolve_Private(const PC_HPDDM_Level *ctx, PetscScalar *rhs, const unsigned short &mu)
1089 {
1090   Mat      B, X;
1091   PetscInt n, N, j = 0;
1092 
1093   PetscFunctionBegin;
1094   PetscCall(KSPGetOperators(ctx->ksp, &B, nullptr));
1095   PetscCall(MatGetLocalSize(B, &n, nullptr));
1096   PetscCall(MatGetSize(B, &N, nullptr));
1097   if (ctx->parent->log_separate) {
1098     j = std::distance(ctx->parent->levels, std::find(ctx->parent->levels, ctx->parent->levels + ctx->parent->N, ctx));
1099     PetscCall(PetscLogEventBegin(PC_HPDDM_Solve[j], ctx->ksp, nullptr, nullptr, nullptr));
1100   }
1101   if (mu == 1) {
1102     if (!ctx->ksp->vec_rhs) {
1103       PetscCall(VecCreateMPIWithArray(PetscObjectComm((PetscObject)ctx->ksp), 1, n, N, nullptr, &ctx->ksp->vec_rhs));
1104       PetscCall(VecCreateMPI(PetscObjectComm((PetscObject)ctx->ksp), n, N, &ctx->ksp->vec_sol));
1105     }
1106     PetscCall(VecPlaceArray(ctx->ksp->vec_rhs, rhs));
1107     PetscCall(KSPSolve(ctx->ksp, nullptr, nullptr));
1108     PetscCall(VecCopy(ctx->ksp->vec_sol, ctx->ksp->vec_rhs));
1109     PetscCall(VecResetArray(ctx->ksp->vec_rhs));
1110   } else {
1111     PetscCall(MatCreateDense(PetscObjectComm((PetscObject)ctx->ksp), n, PETSC_DECIDE, N, mu, rhs, &B));
1112     PetscCall(MatCreateDense(PetscObjectComm((PetscObject)ctx->ksp), n, PETSC_DECIDE, N, mu, nullptr, &X));
1113     PetscCall(KSPMatSolve(ctx->ksp, B, X));
1114     PetscCall(MatCopy(X, B, SAME_NONZERO_PATTERN));
1115     PetscCall(MatDestroy(&X));
1116     PetscCall(MatDestroy(&B));
1117   }
1118   if (ctx->parent->log_separate) PetscCall(PetscLogEventEnd(PC_HPDDM_Solve[j], ctx->ksp, nullptr, nullptr, nullptr));
1119   PetscFunctionReturn(PETSC_SUCCESS);
1120 }
1121 
1122 static PetscErrorCode PCHPDDMSetUpNeumannOverlap_Private(PC pc)
1123 {
1124   PC_HPDDM *data = (PC_HPDDM *)pc->data;
1125 
1126   PetscFunctionBegin;
1127   if (data->setup) {
1128     Mat       P;
1129     Vec       x, xt = nullptr;
1130     PetscReal t = 0.0, s = 0.0;
1131 
1132     PetscCall(PCGetOperators(pc, nullptr, &P));
1133     PetscCall(PetscObjectQuery((PetscObject)P, "__SNES_latest_X", (PetscObject *)&x));
1134     PetscCallBack("PCHPDDM Neumann callback", (*data->setup)(data->aux, t, x, xt, s, data->is, data->setup_ctx));
1135   }
1136   PetscFunctionReturn(PETSC_SUCCESS);
1137 }
1138 
1139 static PetscErrorCode PCHPDDMCreateSubMatrices_Private(Mat mat, PetscInt n, const IS *, const IS *, MatReuse scall, Mat *submat[])
1140 {
1141   Mat       A;
1142   PetscBool flg;
1143 
1144   PetscFunctionBegin;
1145   PetscCheck(n == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "MatCreateSubMatrices() called to extract %" PetscInt_FMT " submatrices, which is different than 1", n);
1146   /* previously composed Mat */
1147   PetscCall(PetscObjectQuery((PetscObject)mat, "_PCHPDDM_SubMatrices", (PetscObject *)&A));
1148   PetscCheck(A, PETSC_COMM_SELF, PETSC_ERR_PLIB, "SubMatrices not found in Mat");
1149   PetscCall(PetscObjectTypeCompare((PetscObject)A, MATSCHURCOMPLEMENT, &flg)); /* MATSCHURCOMPLEMENT has neither a MatDuplicate() nor a MatCopy() implementation */
1150   if (scall == MAT_INITIAL_MATRIX) {
1151     PetscCall(PetscCalloc1(2, submat)); /* allocate an extra Mat to avoid errors in MatDestroySubMatrices_Dummy() */
1152     if (!flg) PetscCall(MatDuplicate(A, MAT_COPY_VALUES, *submat));
1153   } else if (!flg) PetscCall(MatCopy(A, (*submat)[0], SAME_NONZERO_PATTERN));
1154   if (flg) {
1155     PetscCall(MatDestroy(*submat)); /* previously created Mat has to be destroyed */
1156     (*submat)[0] = A;
1157     PetscCall(PetscObjectReference((PetscObject)A));
1158   }
1159   PetscFunctionReturn(PETSC_SUCCESS);
1160 }
1161 
1162 static PetscErrorCode PCHPDDMCommunicationAvoidingPCASM_Private(PC pc, Mat C, PetscBool sorted)
1163 {
1164   void (*op)(void);
1165 
1166   PetscFunctionBegin;
1167   /* previously-composed Mat */
1168   PetscCall(PetscObjectCompose((PetscObject)pc->pmat, "_PCHPDDM_SubMatrices", (PetscObject)C));
1169   PetscCall(MatGetOperation(pc->pmat, MATOP_CREATE_SUBMATRICES, &op));
1170   /* trick suggested by Barry https://lists.mcs.anl.gov/pipermail/petsc-dev/2020-January/025491.html */
1171   PetscCall(MatSetOperation(pc->pmat, MATOP_CREATE_SUBMATRICES, (void (*)(void))PCHPDDMCreateSubMatrices_Private));
1172   if (sorted) PetscCall(PCASMSetSortIndices(pc, PETSC_FALSE)); /* everything is already sorted */
1173   PetscCall(PCSetFromOptions(pc));                             /* otherwise -pc_hpddm_levels_1_pc_asm_sub_mat_type is not used */
1174   PetscCall(PCSetUp(pc));
1175   /* reset MatCreateSubMatrices() */
1176   PetscCall(MatSetOperation(pc->pmat, MATOP_CREATE_SUBMATRICES, op));
1177   PetscCall(PetscObjectCompose((PetscObject)pc->pmat, "_PCHPDDM_SubMatrices", nullptr));
1178   PetscFunctionReturn(PETSC_SUCCESS);
1179 }
1180 
1181 static PetscErrorCode PCHPDDMPermute_Private(IS is, IS in_is, IS *out_is, Mat in_C, Mat *out_C, IS *p)
1182 {
1183   IS                           perm;
1184   const PetscInt              *ptr;
1185   PetscInt                    *concatenate, size, bs;
1186   std::map<PetscInt, PetscInt> order;
1187   PetscBool                    sorted;
1188 
1189   PetscFunctionBegin;
1190   PetscValidHeaderSpecific(is, IS_CLASSID, 1);
1191   PetscValidHeaderSpecific(in_C, MAT_CLASSID, 4);
1192   PetscCall(ISSorted(is, &sorted));
1193   if (!sorted) {
1194     PetscCall(ISGetLocalSize(is, &size));
1195     PetscCall(ISGetIndices(is, &ptr));
1196     PetscCall(ISGetBlockSize(is, &bs));
1197     /* MatCreateSubMatrices(), called by PCASM, follows the global numbering of Pmat */
1198     for (PetscInt n = 0; n < size; n += bs) order.insert(std::make_pair(ptr[n] / bs, n / bs));
1199     PetscCall(ISRestoreIndices(is, &ptr));
1200     size /= bs;
1201     if (out_C) {
1202       PetscCall(PetscMalloc1(size, &concatenate));
1203       for (const std::pair<const PetscInt, PetscInt> &i : order) *concatenate++ = i.second;
1204       concatenate -= size;
1205       PetscCall(ISCreateBlock(PetscObjectComm((PetscObject)in_C), bs, size, concatenate, PETSC_OWN_POINTER, &perm));
1206       PetscCall(ISSetPermutation(perm));
1207       /* permute user-provided Mat so that it matches with MatCreateSubMatrices() numbering */
1208       PetscCall(MatPermute(in_C, perm, perm, out_C));
1209       if (p) *p = perm;
1210       else PetscCall(ISDestroy(&perm)); /* no need to save the permutation */
1211     }
1212     if (out_is) {
1213       PetscCall(PetscMalloc1(size, &concatenate));
1214       for (const std::pair<const PetscInt, PetscInt> &i : order) *concatenate++ = i.first;
1215       concatenate -= size;
1216       /* permute user-provided IS so that it matches with MatCreateSubMatrices() numbering */
1217       PetscCall(ISCreateBlock(PetscObjectComm((PetscObject)in_is), bs, size, concatenate, PETSC_OWN_POINTER, out_is));
1218     }
1219   } else { /* input IS is sorted, nothing to permute, simply duplicate inputs when needed */
1220     if (out_C) PetscCall(MatDuplicate(in_C, MAT_COPY_VALUES, out_C));
1221     if (out_is) PetscCall(ISDuplicate(in_is, out_is));
1222   }
1223   PetscFunctionReturn(PETSC_SUCCESS);
1224 }
1225 
1226 static PetscErrorCode PCHPDDMCheckSymmetry_Private(PC pc, Mat A01, Mat A10, Mat *B01 = nullptr)
1227 {
1228   Mat       T, U = nullptr, B = nullptr;
1229   IS        z;
1230   PetscBool flg, conjugate = PETSC_FALSE;
1231 
1232   PetscFunctionBegin;
1233   PetscCall(PetscObjectTypeCompare((PetscObject)A10, MATTRANSPOSEVIRTUAL, &flg));
1234   if (B01) *B01 = nullptr;
1235   if (flg) {
1236     PetscCall(MatShellGetScalingShifts(A10, (PetscScalar *)MAT_SHELL_NOT_ALLOWED, (PetscScalar *)MAT_SHELL_NOT_ALLOWED, (Vec *)MAT_SHELL_NOT_ALLOWED, (Vec *)MAT_SHELL_NOT_ALLOWED, (Vec *)MAT_SHELL_NOT_ALLOWED, (Mat *)MAT_SHELL_NOT_ALLOWED, (IS *)MAT_SHELL_NOT_ALLOWED, (IS *)MAT_SHELL_NOT_ALLOWED));
1237     PetscCall(MatTransposeGetMat(A10, &U));
1238   } else {
1239     PetscCall(PetscObjectTypeCompare((PetscObject)A10, MATHERMITIANTRANSPOSEVIRTUAL, &flg));
1240     if (flg) {
1241       PetscCall(MatShellGetScalingShifts(A10, (PetscScalar *)MAT_SHELL_NOT_ALLOWED, (PetscScalar *)MAT_SHELL_NOT_ALLOWED, (Vec *)MAT_SHELL_NOT_ALLOWED, (Vec *)MAT_SHELL_NOT_ALLOWED, (Vec *)MAT_SHELL_NOT_ALLOWED, (Mat *)MAT_SHELL_NOT_ALLOWED, (IS *)MAT_SHELL_NOT_ALLOWED, (IS *)MAT_SHELL_NOT_ALLOWED));
1242       PetscCall(MatHermitianTransposeGetMat(A10, &U));
1243       conjugate = PETSC_TRUE;
1244     }
1245   }
1246   if (U) PetscCall(MatDuplicate(U, MAT_COPY_VALUES, &T));
1247   else PetscCall(MatHermitianTranspose(A10, MAT_INITIAL_MATRIX, &T));
1248   PetscCall(PetscObjectTypeCompare((PetscObject)A01, MATTRANSPOSEVIRTUAL, &flg));
1249   if (flg) {
1250     PetscCall(MatShellGetScalingShifts(A01, (PetscScalar *)MAT_SHELL_NOT_ALLOWED, (PetscScalar *)MAT_SHELL_NOT_ALLOWED, (Vec *)MAT_SHELL_NOT_ALLOWED, (Vec *)MAT_SHELL_NOT_ALLOWED, (Vec *)MAT_SHELL_NOT_ALLOWED, (Mat *)MAT_SHELL_NOT_ALLOWED, (IS *)MAT_SHELL_NOT_ALLOWED, (IS *)MAT_SHELL_NOT_ALLOWED));
1251     PetscCall(MatTransposeGetMat(A01, &A01));
1252     PetscCall(MatTranspose(A01, MAT_INITIAL_MATRIX, &B));
1253     A01 = B;
1254   } else {
1255     PetscCall(PetscObjectTypeCompare((PetscObject)A01, MATHERMITIANTRANSPOSEVIRTUAL, &flg));
1256     if (flg) {
1257       PetscCall(MatShellGetScalingShifts(A01, (PetscScalar *)MAT_SHELL_NOT_ALLOWED, (PetscScalar *)MAT_SHELL_NOT_ALLOWED, (Vec *)MAT_SHELL_NOT_ALLOWED, (Vec *)MAT_SHELL_NOT_ALLOWED, (Vec *)MAT_SHELL_NOT_ALLOWED, (Mat *)MAT_SHELL_NOT_ALLOWED, (IS *)MAT_SHELL_NOT_ALLOWED, (IS *)MAT_SHELL_NOT_ALLOWED));
1258       PetscCall(MatHermitianTransposeGetMat(A01, &A01));
1259       PetscCall(MatHermitianTranspose(A01, MAT_INITIAL_MATRIX, &B));
1260       A01 = B;
1261     }
1262   }
1263   PetscCall(PetscLayoutCompare(T->rmap, A01->rmap, &flg));
1264   if (flg) {
1265     PetscCall(PetscLayoutCompare(T->cmap, A01->cmap, &flg));
1266     if (flg) {
1267       PetscCall(MatFindZeroRows(A01, &z)); /* for essential boundary conditions, some implementations will */
1268       if (z) {                             /*  zero rows in [P00 A01] except for the diagonal of P00       */
1269         if (B01) PetscCall(MatDuplicate(T, MAT_COPY_VALUES, B01));
1270         PetscCall(MatSetOption(T, MAT_NO_OFF_PROC_ZERO_ROWS, PETSC_TRUE));
1271         PetscCall(MatZeroRowsIS(T, z, 0.0, nullptr, nullptr)); /* corresponding zero rows from A01 */
1272       }
1273       PetscCall(MatMultEqual(A01, T, 20, &flg));
1274       if (!B01) PetscCheck(flg, PetscObjectComm((PetscObject)pc), PETSC_ERR_SUP, "A01 != A10^T");
1275       else {
1276         PetscCall(PetscInfo(pc, "A01 and A10^T are equal? %s\n", PetscBools[flg]));
1277         if (!flg) {
1278           if (z) PetscCall(MatDestroy(&T));
1279           else *B01 = T;
1280           flg = PETSC_TRUE;
1281         } else PetscCall(MatDestroy(B01));
1282       }
1283       PetscCall(ISDestroy(&z));
1284     }
1285   }
1286   if (!flg) PetscCall(PetscInfo(pc, "A01 and A10^T have non-congruent layouts, cannot test for equality\n"));
1287   if (!B01 || !*B01) PetscCall(MatDestroy(&T));
1288   else if (conjugate) PetscCall(MatConjugate(T));
1289   PetscCall(MatDestroy(&B));
1290   PetscFunctionReturn(PETSC_SUCCESS);
1291 }
1292 
1293 static PetscErrorCode PCHPDDMCheckInclusion_Private(PC pc, IS is, IS is_local, PetscBool check)
1294 {
1295   IS          intersect;
1296   const char *str = "IS of the auxiliary Mat does not include all local rows of A";
1297   PetscBool   equal;
1298 
1299   PetscFunctionBegin;
1300   PetscCall(ISIntersect(is, is_local, &intersect));
1301   PetscCall(ISEqualUnsorted(is_local, intersect, &equal));
1302   PetscCall(ISDestroy(&intersect));
1303   if (check) PetscCheck(equal, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "%s", str);
1304   else if (!equal) PetscCall(PetscInfo(pc, "%s\n", str));
1305   PetscFunctionReturn(PETSC_SUCCESS);
1306 }
1307 
1308 static PetscErrorCode PCHPDDMDestroySubMatrices_Private(PetscBool flg, PetscBool algebraic, Mat *sub)
1309 {
1310   IS is;
1311 
1312   PetscFunctionBegin;
1313   if (!flg) {
1314     if (algebraic) {
1315       PetscCall(PetscObjectQuery((PetscObject)sub[0], "_PCHPDDM_Embed", (PetscObject *)&is));
1316       PetscCall(ISDestroy(&is));
1317       PetscCall(PetscObjectCompose((PetscObject)sub[0], "_PCHPDDM_Embed", nullptr));
1318       PetscCall(PetscObjectCompose((PetscObject)sub[0], "_PCHPDDM_Compact", nullptr));
1319     }
1320     PetscCall(MatDestroySubMatrices(algebraic ? 2 : 1, &sub));
1321   }
1322   PetscFunctionReturn(PETSC_SUCCESS);
1323 }
1324 
1325 static PetscErrorCode PCHPDDMAlgebraicAuxiliaryMat_Private(Mat P, IS *is, Mat *sub[], PetscBool block)
1326 {
1327   IS         icol[3], irow[2];
1328   Mat       *M, Q;
1329   PetscReal *ptr;
1330   PetscInt  *idx, p = 0, bs = P->cmap->bs;
1331   PetscBool  flg;
1332 
1333   PetscFunctionBegin;
1334   PetscCall(ISCreateStride(PETSC_COMM_SELF, P->cmap->N, 0, 1, icol + 2));
1335   PetscCall(ISSetBlockSize(icol[2], bs));
1336   PetscCall(ISSetIdentity(icol[2]));
1337   PetscCall(PetscObjectTypeCompare((PetscObject)P, MATMPISBAIJ, &flg));
1338   if (flg) {
1339     /* MatCreateSubMatrices() does not handle MATMPISBAIJ properly when iscol != isrow, so convert first to MATMPIBAIJ */
1340     PetscCall(MatConvert(P, MATMPIBAIJ, MAT_INITIAL_MATRIX, &Q));
1341     std::swap(P, Q);
1342   }
1343   PetscCall(MatCreateSubMatrices(P, 1, is, icol + 2, MAT_INITIAL_MATRIX, &M));
1344   if (flg) {
1345     std::swap(P, Q);
1346     PetscCall(MatDestroy(&Q));
1347   }
1348   PetscCall(ISDestroy(icol + 2));
1349   PetscCall(ISCreateStride(PETSC_COMM_SELF, M[0]->rmap->N, 0, 1, irow));
1350   PetscCall(ISSetBlockSize(irow[0], bs));
1351   PetscCall(ISSetIdentity(irow[0]));
1352   if (!block) {
1353     PetscCall(PetscMalloc2(P->cmap->N, &ptr, P->cmap->N / bs, &idx));
1354     PetscCall(MatGetColumnNorms(M[0], NORM_INFINITY, ptr));
1355     /* check for nonzero columns so that M[0] may be expressed in compact form */
1356     for (PetscInt n = 0; n < P->cmap->N; n += bs) {
1357       if (std::find_if(ptr + n, ptr + n + bs, [](PetscReal v) { return v > PETSC_MACHINE_EPSILON; }) != ptr + n + bs) idx[p++] = n / bs;
1358     }
1359     PetscCall(ISCreateBlock(PETSC_COMM_SELF, bs, p, idx, PETSC_USE_POINTER, icol + 1));
1360     PetscCall(ISSetInfo(icol[1], IS_SORTED, IS_GLOBAL, PETSC_TRUE, PETSC_TRUE));
1361     PetscCall(ISEmbed(*is, icol[1], PETSC_FALSE, icol + 2));
1362     irow[1] = irow[0];
1363     /* first Mat will be used in PCASM (if it is used as a PC on this level) and as the left-hand side of GenEO */
1364     icol[0] = is[0];
1365     PetscCall(MatCreateSubMatrices(M[0], 2, irow, icol, MAT_INITIAL_MATRIX, sub));
1366     PetscCall(ISDestroy(icol + 1));
1367     PetscCall(PetscFree2(ptr, idx));
1368     /* IS used to go back and forth between the augmented and the original local linear system, see eq. (3.4) of [2022b] */
1369     PetscCall(PetscObjectCompose((PetscObject)(*sub)[0], "_PCHPDDM_Embed", (PetscObject)icol[2]));
1370     /* Mat used in eq. (3.1) of [2022b] */
1371     PetscCall(PetscObjectCompose((PetscObject)(*sub)[0], "_PCHPDDM_Compact", (PetscObject)(*sub)[1]));
1372   } else {
1373     Mat aux;
1374 
1375     PetscCall(MatSetOption(M[0], MAT_SUBMAT_SINGLEIS, PETSC_TRUE));
1376     /* diagonal block of the overlapping rows */
1377     PetscCall(MatCreateSubMatrices(M[0], 1, irow, is, MAT_INITIAL_MATRIX, sub));
1378     PetscCall(MatDuplicate((*sub)[0], MAT_COPY_VALUES, &aux));
1379     PetscCall(MatSetOption(aux, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_FALSE));
1380     if (bs == 1) { /* scalar case */
1381       Vec sum[2];
1382 
1383       PetscCall(MatCreateVecs(aux, sum, sum + 1));
1384       PetscCall(MatGetRowSum(M[0], sum[0]));
1385       PetscCall(MatGetRowSum(aux, sum[1]));
1386       /* off-diagonal block row sum (full rows - diagonal block rows) */
1387       PetscCall(VecAXPY(sum[0], -1.0, sum[1]));
1388       /* subdomain matrix plus off-diagonal block row sum */
1389       PetscCall(MatDiagonalSet(aux, sum[0], ADD_VALUES));
1390       PetscCall(VecDestroy(sum));
1391       PetscCall(VecDestroy(sum + 1));
1392     } else { /* vectorial case */
1393       /* TODO: missing MatGetValuesBlocked(), so the code below is     */
1394       /* an extension of the scalar case for when bs > 1, but it could */
1395       /* be more efficient by avoiding all these MatMatMult()          */
1396       Mat          sum[2], ones;
1397       PetscScalar *ptr;
1398 
1399       PetscCall(PetscCalloc1(M[0]->cmap->n * bs, &ptr));
1400       PetscCall(MatCreateDense(PETSC_COMM_SELF, M[0]->cmap->n, bs, M[0]->cmap->n, bs, ptr, &ones));
1401       for (PetscInt n = 0; n < M[0]->cmap->n; n += bs) {
1402         for (p = 0; p < bs; ++p) ptr[n + p * (M[0]->cmap->n + 1)] = 1.0;
1403       }
1404       PetscCall(MatMatMult(M[0], ones, MAT_INITIAL_MATRIX, PETSC_CURRENT, sum));
1405       PetscCall(MatDestroy(&ones));
1406       PetscCall(MatCreateDense(PETSC_COMM_SELF, aux->cmap->n, bs, aux->cmap->n, bs, ptr, &ones));
1407       PetscCall(MatDenseSetLDA(ones, M[0]->cmap->n));
1408       PetscCall(MatMatMult(aux, ones, MAT_INITIAL_MATRIX, PETSC_CURRENT, sum + 1));
1409       PetscCall(MatDestroy(&ones));
1410       PetscCall(PetscFree(ptr));
1411       /* off-diagonal block row sum (full rows - diagonal block rows) */
1412       PetscCall(MatAXPY(sum[0], -1.0, sum[1], SAME_NONZERO_PATTERN));
1413       PetscCall(MatDestroy(sum + 1));
1414       /* re-order values to be consistent with MatSetValuesBlocked()           */
1415       /* equivalent to MatTranspose() which does not truly handle              */
1416       /* MAT_INPLACE_MATRIX in the rectangular case, as it calls PetscMalloc() */
1417       PetscCall(MatDenseGetArrayWrite(sum[0], &ptr));
1418       HPDDM::Wrapper<PetscScalar>::imatcopy<'T'>(bs, sum[0]->rmap->n, ptr, sum[0]->rmap->n, bs);
1419       /* subdomain matrix plus off-diagonal block row sum */
1420       for (PetscInt n = 0; n < aux->cmap->n / bs; ++n) PetscCall(MatSetValuesBlocked(aux, 1, &n, 1, &n, ptr + n * bs * bs, ADD_VALUES));
1421       PetscCall(MatAssemblyBegin(aux, MAT_FINAL_ASSEMBLY));
1422       PetscCall(MatAssemblyEnd(aux, MAT_FINAL_ASSEMBLY));
1423       PetscCall(MatDenseRestoreArrayWrite(sum[0], &ptr));
1424       PetscCall(MatDestroy(sum));
1425     }
1426     PetscCall(MatSetOption(aux, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_TRUE));
1427     /* left-hand side of GenEO, with the same sparsity pattern as PCASM subdomain solvers  */
1428     PetscCall(PetscObjectCompose((PetscObject)(*sub)[0], "_PCHPDDM_Neumann_Mat", (PetscObject)aux));
1429   }
1430   PetscCall(ISDestroy(irow));
1431   PetscCall(MatDestroySubMatrices(1, &M));
1432   PetscFunctionReturn(PETSC_SUCCESS);
1433 }
1434 
1435 static PetscErrorCode PCApply_Nest(PC pc, Vec x, Vec y)
1436 {
1437   Mat                    A;
1438   MatSolverType          type;
1439   IS                     is[2];
1440   PetscBool              flg;
1441   std::pair<PC, Vec[2]> *p;
1442 
1443   PetscFunctionBegin;
1444   PetscCall(PCShellGetContext(pc, &p));
1445   PetscCall(PCGetOperators(p->first, &A, nullptr));
1446   PetscCall(MatNestGetISs(A, is, nullptr));
1447   PetscCall(PCFactorGetMatSolverType(p->first, &type));
1448   PetscCall(PCFactorGetMatrix(p->first, &A));
1449   PetscCall(PetscStrcmp(type, MATSOLVERMUMPS, &flg));
1450   if (flg && A->schur) {
1451 #if PetscDefined(HAVE_MUMPS)
1452     PetscCall(MatMumpsSetIcntl(A, 26, 1)); /* reduction/condensation phase followed by Schur complement solve */
1453 #endif
1454   }
1455   PetscCall(VecISCopy(p->second[0], is[1], SCATTER_FORWARD, x)); /* assign the RHS associated to the Schur complement */
1456   PetscCall(PCApply(p->first, p->second[0], p->second[1]));
1457   PetscCall(VecISCopy(p->second[1], is[1], SCATTER_REVERSE, y)); /* retrieve the partial solution associated to the Schur complement */
1458   if (flg) {
1459 #if PetscDefined(HAVE_MUMPS)
1460     PetscCall(MatMumpsSetIcntl(A, 26, -1)); /* default ICNTL(26) value in PETSc */
1461 #endif
1462   }
1463   PetscFunctionReturn(PETSC_SUCCESS);
1464 }
1465 
1466 static PetscErrorCode PCView_Nest(PC pc, PetscViewer viewer)
1467 {
1468   std::pair<PC, Vec[2]> *p;
1469 
1470   PetscFunctionBegin;
1471   PetscCall(PCShellGetContext(pc, &p));
1472   PetscCall(PCView(p->first, viewer));
1473   PetscFunctionReturn(PETSC_SUCCESS);
1474 }
1475 
1476 static PetscErrorCode PCDestroy_Nest(PC pc)
1477 {
1478   std::pair<PC, Vec[2]> *p;
1479 
1480   PetscFunctionBegin;
1481   PetscCall(PCShellGetContext(pc, &p));
1482   PetscCall(VecDestroy(p->second));
1483   PetscCall(VecDestroy(p->second + 1));
1484   PetscCall(PCDestroy(&p->first));
1485   PetscCall(PetscFree(p));
1486   PetscFunctionReturn(PETSC_SUCCESS);
1487 }
1488 
1489 template <bool T = false>
1490 static PetscErrorCode MatMult_Schur(Mat A, Vec x, Vec y)
1491 {
1492   std::tuple<Mat, PetscSF, Vec[2]> *ctx;
1493 
1494   PetscFunctionBegin;
1495   PetscCall(MatShellGetContext(A, &ctx));
1496   PetscCall(VecScatterBegin(std::get<1>(*ctx), x, std::get<2>(*ctx)[0], INSERT_VALUES, SCATTER_FORWARD)); /* local Vec with overlap */
1497   PetscCall(VecScatterEnd(std::get<1>(*ctx), x, std::get<2>(*ctx)[0], INSERT_VALUES, SCATTER_FORWARD));
1498   if (!T) PetscCall(MatMult(std::get<0>(*ctx), std::get<2>(*ctx)[0], std::get<2>(*ctx)[1])); /* local Schur complement */
1499   else PetscCall(MatMultTranspose(std::get<0>(*ctx), std::get<2>(*ctx)[0], std::get<2>(*ctx)[1]));
1500   PetscCall(VecSet(y, 0.0));
1501   PetscCall(VecScatterBegin(std::get<1>(*ctx), std::get<2>(*ctx)[1], y, ADD_VALUES, SCATTER_REVERSE)); /* global Vec with summed up contributions on the overlap */
1502   PetscCall(VecScatterEnd(std::get<1>(*ctx), std::get<2>(*ctx)[1], y, ADD_VALUES, SCATTER_REVERSE));
1503   PetscFunctionReturn(PETSC_SUCCESS);
1504 }
1505 
1506 static PetscErrorCode MatDestroy_Schur(Mat A)
1507 {
1508   std::tuple<Mat, PetscSF, Vec[2]> *ctx;
1509 
1510   PetscFunctionBegin;
1511   PetscCall(MatShellGetContext(A, &ctx));
1512   PetscCall(VecDestroy(std::get<2>(*ctx)));
1513   PetscCall(VecDestroy(std::get<2>(*ctx) + 1));
1514   PetscCall(PetscFree(ctx));
1515   PetscFunctionReturn(PETSC_SUCCESS);
1516 }
1517 
1518 static PetscErrorCode MatMult_SchurCorrection(Mat A, Vec x, Vec y)
1519 {
1520   PC                                         pc;
1521   std::tuple<PC[2], Mat[2], PCSide, Vec[3]> *ctx;
1522 
1523   PetscFunctionBegin;
1524   PetscCall(MatShellGetContext(A, &ctx));
1525   pc = ((PC_HPDDM *)std::get<0>(*ctx)[0]->data)->levels[0]->ksp->pc;
1526   if (std::get<2>(*ctx) == PC_LEFT || std::get<2>(*ctx) == PC_SIDE_DEFAULT) {             /* Q_0 is the coarse correction associated to the A00 block from PCFIELDSPLIT */
1527     PetscCall(MatMult(std::get<1>(*ctx)[0], x, std::get<3>(*ctx)[1]));                    /*     A_01 x                 */
1528     PetscCall(PCHPDDMDeflate_Private(pc, std::get<3>(*ctx)[1], std::get<3>(*ctx)[1]));    /*     Q_0 A_01 x             */
1529     PetscCall(MatMult(std::get<1>(*ctx)[1], std::get<3>(*ctx)[1], std::get<3>(*ctx)[0])); /*     A_10 Q_0 A_01 x        */
1530     PetscCall(PCApply(std::get<0>(*ctx)[1], std::get<3>(*ctx)[0], y));                    /* y = M_S^-1 A_10 Q_0 A_01 x */
1531   } else {
1532     PetscCall(PCApply(std::get<0>(*ctx)[1], x, std::get<3>(*ctx)[0]));                    /*     M_S^-1 x               */
1533     PetscCall(MatMult(std::get<1>(*ctx)[0], std::get<3>(*ctx)[0], std::get<3>(*ctx)[1])); /*     A_01 M_S^-1 x          */
1534     PetscCall(PCHPDDMDeflate_Private(pc, std::get<3>(*ctx)[1], std::get<3>(*ctx)[1]));    /*     Q_0 A_01 M_S^-1 x      */
1535     PetscCall(MatMult(std::get<1>(*ctx)[1], std::get<3>(*ctx)[1], y));                    /* y = A_10 Q_0 A_01 M_S^-1 x */
1536   }
1537   PetscCall(VecAXPY(y, -1.0, x)); /* y -= x, preconditioned eq. (24) of https://hal.science/hal-02343808v6/document (with a sign flip) */
1538   PetscFunctionReturn(PETSC_SUCCESS);
1539 }
1540 
1541 static PetscErrorCode MatView_SchurCorrection(Mat A, PetscViewer viewer)
1542 {
1543   PetscBool                                  ascii;
1544   std::tuple<PC[2], Mat[2], PCSide, Vec[3]> *ctx;
1545 
1546   PetscFunctionBegin;
1547   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &ascii));
1548   if (ascii) {
1549     PetscCall(MatShellGetContext(A, &ctx));
1550     PetscCall(PetscViewerASCIIPrintf(viewer, "action of %s\n", std::get<2>(*ctx) == PC_LEFT || std::get<2>(*ctx) == PC_SIDE_DEFAULT ? "(I - M_S^-1 A_10 Q_0 A_01)" : "(I - A_10 Q_0 A_01 M_S^-1)"));
1551     PetscCall(PCView(std::get<0>(*ctx)[1], viewer)); /* no need to PCView(Q_0) since it will be done by PCFIELDSPLIT */
1552   }
1553   PetscFunctionReturn(PETSC_SUCCESS);
1554 }
1555 
1556 static PetscErrorCode MatDestroy_SchurCorrection(Mat A)
1557 {
1558   std::tuple<PC[2], Mat[2], PCSide, Vec[3]> *ctx;
1559 
1560   PetscFunctionBegin;
1561   PetscCall(MatShellGetContext(A, &ctx));
1562   PetscCall(VecDestroy(std::get<3>(*ctx)));
1563   PetscCall(VecDestroy(std::get<3>(*ctx) + 1));
1564   PetscCall(VecDestroy(std::get<3>(*ctx) + 2));
1565   PetscCall(PCDestroy(std::get<0>(*ctx) + 1));
1566   PetscCall(PetscFree(ctx));
1567   PetscFunctionReturn(PETSC_SUCCESS);
1568 }
1569 
1570 static PetscErrorCode PCPostSolve_SchurPreLeastSquares(PC, KSP, Vec, Vec x)
1571 {
1572   PetscFunctionBegin;
1573   PetscCall(VecScale(x, -1.0));
1574   PetscFunctionReturn(PETSC_SUCCESS);
1575 }
1576 
1577 static PetscErrorCode KSPPreSolve_SchurCorrection(KSP, Vec b, Vec, void *context)
1578 {
1579   std::tuple<PC[2], Mat[2], PCSide, Vec[3]> *ctx = reinterpret_cast<std::tuple<PC[2], Mat[2], PCSide, Vec[3]> *>(context);
1580 
1581   PetscFunctionBegin;
1582   if (std::get<2>(*ctx) == PC_LEFT || std::get<2>(*ctx) == PC_SIDE_DEFAULT) {
1583     PetscCall(PCApply(std::get<0>(*ctx)[1], b, std::get<3>(*ctx)[2]));
1584     std::swap(*b, *std::get<3>(*ctx)[2]); /* replace b by M^-1 b, but need to keep a copy of the original RHS, so swap it with the work Vec */
1585   }
1586   PetscFunctionReturn(PETSC_SUCCESS);
1587 }
1588 
1589 static PetscErrorCode KSPPostSolve_SchurCorrection(KSP, Vec b, Vec x, void *context)
1590 {
1591   std::tuple<PC[2], Mat[2], PCSide, Vec[3]> *ctx = reinterpret_cast<std::tuple<PC[2], Mat[2], PCSide, Vec[3]> *>(context);
1592 
1593   PetscFunctionBegin;
1594   if (std::get<2>(*ctx) == PC_LEFT || std::get<2>(*ctx) == PC_SIDE_DEFAULT) std::swap(*b, *std::get<3>(*ctx)[2]); /* put back the original RHS where it belongs */
1595   else {
1596     PetscCall(PCApply(std::get<0>(*ctx)[1], x, std::get<3>(*ctx)[2]));
1597     PetscCall(VecCopy(std::get<3>(*ctx)[2], x)); /* replace x by M^-1 x */
1598   }
1599   PetscFunctionReturn(PETSC_SUCCESS);
1600 }
1601 
1602 static PetscErrorCode MatMult_Harmonic(Mat, Vec, Vec);
1603 static PetscErrorCode MatMultTranspose_Harmonic(Mat, Vec, Vec);
1604 static PetscErrorCode MatProduct_AB_Harmonic(Mat, Mat, Mat, void *);
1605 static PetscErrorCode MatDestroy_Harmonic(Mat);
1606 
1607 static PetscErrorCode PCSetUp_HPDDM(PC pc)
1608 {
1609   PC_HPDDM                                  *data = (PC_HPDDM *)pc->data;
1610   PC                                         inner;
1611   KSP                                       *ksp;
1612   Mat                                       *sub, A, P, N, C = nullptr, uaux = nullptr, weighted, subA[2], S;
1613   Vec                                        xin, v;
1614   std::vector<Vec>                           initial;
1615   IS                                         is[1], loc, uis = data->is, unsorted = nullptr;
1616   ISLocalToGlobalMapping                     l2g;
1617   char                                       prefix[256];
1618   const char                                *pcpre;
1619   const PetscScalar *const                  *ev;
1620   PetscInt                                   n, requested = data->N, reused = 0, overlap = -1;
1621   MatStructure                               structure  = UNKNOWN_NONZERO_PATTERN;
1622   PetscBool                                  subdomains = PETSC_FALSE, flg = PETSC_FALSE, ismatis, swap = PETSC_FALSE, algebraic = PETSC_FALSE, block = PETSC_FALSE;
1623   DM                                         dm;
1624   std::tuple<PC[2], Mat[2], PCSide, Vec[3]> *ctx = nullptr;
1625 #if PetscDefined(USE_DEBUG)
1626   IS  dis  = nullptr;
1627   Mat daux = nullptr;
1628 #endif
1629 
1630   PetscFunctionBegin;
1631   PetscCheck(data->levels && data->levels[0], PETSC_COMM_SELF, PETSC_ERR_PLIB, "Not a single level allocated");
1632   PetscCall(PCGetOptionsPrefix(pc, &pcpre));
1633   PetscCall(PCGetOperators(pc, &A, &P));
1634   if (!data->levels[0]->ksp) {
1635     PetscCall(KSPCreate(PetscObjectComm((PetscObject)pc), &data->levels[0]->ksp));
1636     PetscCall(KSPSetNestLevel(data->levels[0]->ksp, pc->kspnestlevel));
1637     PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "%spc_hpddm_%s_", pcpre ? pcpre : "", data->N > 1 ? "levels_1" : "coarse"));
1638     PetscCall(KSPSetOptionsPrefix(data->levels[0]->ksp, prefix));
1639     PetscCall(KSPSetType(data->levels[0]->ksp, KSPPREONLY));
1640   } else if (data->levels[0]->ksp->pc && data->levels[0]->ksp->pc->setupcalled == 1 && data->levels[0]->ksp->pc->reusepreconditioner) {
1641     /* if the fine-level PCSHELL exists, its setup has succeeded, and one wants to reuse it, */
1642     /* then just propagate the appropriate flag to the coarser levels                        */
1643     for (n = 0; n < PETSC_PCHPDDM_MAXLEVELS && data->levels[n]; ++n) {
1644       /* the following KSP and PC may be NULL for some processes, hence the check            */
1645       if (data->levels[n]->ksp) PetscCall(KSPSetReusePreconditioner(data->levels[n]->ksp, PETSC_TRUE));
1646       if (data->levels[n]->pc) PetscCall(PCSetReusePreconditioner(data->levels[n]->pc, PETSC_TRUE));
1647     }
1648     /* early bail out because there is nothing to do */
1649     PetscFunctionReturn(PETSC_SUCCESS);
1650   } else {
1651     /* reset coarser levels */
1652     for (n = 1; n < PETSC_PCHPDDM_MAXLEVELS && data->levels[n]; ++n) {
1653       if (data->levels[n]->ksp && data->levels[n]->ksp->pc && data->levels[n]->ksp->pc->setupcalled == 1 && data->levels[n]->ksp->pc->reusepreconditioner && n < data->N) {
1654         reused = data->N - n;
1655         break;
1656       }
1657       PetscCall(KSPDestroy(&data->levels[n]->ksp));
1658       PetscCall(PCDestroy(&data->levels[n]->pc));
1659     }
1660     /* check if some coarser levels are being reused */
1661     PetscCallMPI(MPIU_Allreduce(MPI_IN_PLACE, &reused, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)pc)));
1662     const int *addr = data->levels[0]->P ? data->levels[0]->P->getAddrLocal() : &HPDDM::i__0;
1663 
1664     if (addr != &HPDDM::i__0 && reused != data->N - 1) {
1665       /* reuse previously computed eigenvectors */
1666       ev = data->levels[0]->P->getVectors();
1667       if (ev) {
1668         initial.reserve(*addr);
1669         PetscCall(VecCreateSeqWithArray(PETSC_COMM_SELF, 1, data->levels[0]->P->getDof(), ev[0], &xin));
1670         for (n = 0; n < *addr; ++n) {
1671           PetscCall(VecDuplicate(xin, &v));
1672           PetscCall(VecPlaceArray(xin, ev[n]));
1673           PetscCall(VecCopy(xin, v));
1674           initial.emplace_back(v);
1675           PetscCall(VecResetArray(xin));
1676         }
1677         PetscCall(VecDestroy(&xin));
1678       }
1679     }
1680   }
1681   data->N -= reused;
1682   PetscCall(KSPSetOperators(data->levels[0]->ksp, A, P));
1683 
1684   PetscCall(PetscObjectTypeCompare((PetscObject)P, MATIS, &ismatis));
1685   if (!data->is && !ismatis) {
1686     PetscErrorCode (*create)(DM, IS *, Mat *, PetscErrorCode (**)(Mat, PetscReal, Vec, Vec, PetscReal, IS, void *), void **) = nullptr;
1687     PetscErrorCode (*usetup)(Mat, PetscReal, Vec, Vec, PetscReal, IS, void *)                                                = nullptr;
1688     void *uctx                                                                                                               = nullptr;
1689 
1690     /* first see if we can get the data from the DM */
1691     PetscCall(MatGetDM(P, &dm));
1692     if (!dm) PetscCall(MatGetDM(A, &dm));
1693     if (!dm) PetscCall(PCGetDM(pc, &dm));
1694     if (dm) { /* this is the hook for DMPLEX for which the auxiliary Mat is the local Neumann matrix */
1695       PetscCall(PetscObjectQueryFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", &create));
1696       if (create) {
1697         PetscCall((*create)(dm, &uis, &uaux, &usetup, &uctx));
1698         if (data->Neumann == PETSC_BOOL3_UNKNOWN) data->Neumann = PETSC_BOOL3_TRUE; /* set the value only if it was not already provided by the user */
1699       }
1700     }
1701     if (!create) {
1702       if (!uis) {
1703         PetscCall(PetscObjectQuery((PetscObject)pc, "_PCHPDDM_Neumann_IS", (PetscObject *)&uis));
1704         PetscCall(PetscObjectReference((PetscObject)uis));
1705       }
1706       if (!uaux) {
1707         PetscCall(PetscObjectQuery((PetscObject)pc, "_PCHPDDM_Neumann_Mat", (PetscObject *)&uaux));
1708         PetscCall(PetscObjectReference((PetscObject)uaux));
1709       }
1710       /* look inside the Pmat instead of the PC, needed for MatSchurComplementComputeExplicitOperator() */
1711       if (!uis) {
1712         PetscCall(PetscObjectQuery((PetscObject)P, "_PCHPDDM_Neumann_IS", (PetscObject *)&uis));
1713         PetscCall(PetscObjectReference((PetscObject)uis));
1714       }
1715       if (!uaux) {
1716         PetscCall(PetscObjectQuery((PetscObject)P, "_PCHPDDM_Neumann_Mat", (PetscObject *)&uaux));
1717         PetscCall(PetscObjectReference((PetscObject)uaux));
1718       }
1719     }
1720     PetscCall(PCHPDDMSetAuxiliaryMat(pc, uis, uaux, usetup, uctx));
1721     PetscCall(MatDestroy(&uaux));
1722     PetscCall(ISDestroy(&uis));
1723   }
1724 
1725   if (!ismatis) {
1726     PetscCall(PCHPDDMSetUpNeumannOverlap_Private(pc));
1727     PetscCall(PetscOptionsGetBool(nullptr, pcpre, "-pc_hpddm_block_splitting", &block, nullptr));
1728     PetscCall(PetscOptionsGetInt(nullptr, pcpre, "-pc_hpddm_harmonic_overlap", &overlap, nullptr));
1729     PetscCall(PetscObjectTypeCompare((PetscObject)P, MATSCHURCOMPLEMENT, &flg));
1730     if (data->is || (data->N > 1 && flg)) {
1731       if (block || overlap != -1) {
1732         PetscCall(ISDestroy(&data->is));
1733         PetscCall(MatDestroy(&data->aux));
1734       } else if (data->N > 1 && flg) {
1735         PCHPDDMSchurPreType type = PC_HPDDM_SCHUR_PRE_GENEO;
1736 
1737         PetscCall(PetscOptionsGetEnum(nullptr, pcpre, "-pc_hpddm_schur_precondition", PCHPDDMSchurPreTypes, (PetscEnum *)&type, &flg));
1738         if (type == PC_HPDDM_SCHUR_PRE_LEAST_SQUARES) {
1739           PetscCall(ISDestroy(&data->is)); /* destroy any previously user-set objects since they will be set automatically */
1740           PetscCall(MatDestroy(&data->aux));
1741         } else if (type == PC_HPDDM_SCHUR_PRE_GENEO) {
1742           PetscContainer container = nullptr;
1743 
1744           PetscCall(PetscObjectQuery((PetscObject)pc, "_PCHPDDM_Schur", (PetscObject *)&container));
1745           if (!container) { /* first call to PCSetUp() on the PC associated to the Schur complement */
1746             PC_HPDDM *data_00;
1747             KSP       ksp, inner_ksp;
1748             PC        pc_00;
1749             Mat       A11 = nullptr;
1750             Vec       d   = nullptr;
1751             char     *prefix;
1752 
1753             PetscCall(MatSchurComplementGetKSP(P, &ksp));
1754             PetscCall(KSPGetPC(ksp, &pc_00));
1755             PetscCall(PetscObjectTypeCompare((PetscObject)pc_00, PCHPDDM, &flg));
1756             PetscCheck(flg, PetscObjectComm((PetscObject)P), PETSC_ERR_ARG_INCOMP, "-%spc_hpddm_schur_precondition %s and -%spc_type %s (!= %s)", pcpre ? pcpre : "", PCHPDDMSchurPreTypes[type], ((PetscObject)pc_00)->prefix ? ((PetscObject)pc_00)->prefix : "",
1757                        ((PetscObject)pc_00)->type_name, PCHPDDM);
1758             data_00 = (PC_HPDDM *)pc_00->data;
1759             PetscCheck(data_00->N == 2, PetscObjectComm((PetscObject)P), PETSC_ERR_ARG_INCOMP, "-%spc_hpddm_schur_precondition %s and %" PetscInt_FMT " level%s instead of 2 for the A00 block -%s", pcpre ? pcpre : "", PCHPDDMSchurPreTypes[type],
1760                        data_00->N, data_00->N > 1 ? "s" : "", ((PetscObject)pc_00)->prefix);
1761             PetscCall(PetscObjectTypeCompare((PetscObject)data_00->levels[0]->pc, PCASM, &flg));
1762             PetscCheck(flg, PetscObjectComm((PetscObject)P), PETSC_ERR_ARG_INCOMP, "-%spc_hpddm_schur_precondition %s and -%spc_type %s (!= %s)", pcpre ? pcpre : "", PCHPDDMSchurPreTypes[type], ((PetscObject)data_00->levels[0]->pc)->prefix,
1763                        ((PetscObject)data_00->levels[0]->pc)->type_name, PCASM);
1764             PetscCall(MatSchurComplementGetSubMatrices(P, nullptr, nullptr, nullptr, nullptr, &A11));
1765             if (PetscDefined(USE_DEBUG) || !data->is) {
1766               Mat A01, A10, B = nullptr, C = nullptr, *sub;
1767 
1768               PetscCall(MatSchurComplementGetSubMatrices(P, &A, nullptr, &A01, &A10, nullptr));
1769               PetscCall(PetscObjectTypeCompare((PetscObject)A10, MATTRANSPOSEVIRTUAL, &flg));
1770               if (flg) {
1771                 PetscCall(MatTransposeGetMat(A10, &C));
1772                 PetscCall(MatTranspose(C, MAT_INITIAL_MATRIX, &B));
1773               } else {
1774                 PetscCall(PetscObjectTypeCompare((PetscObject)A10, MATHERMITIANTRANSPOSEVIRTUAL, &flg));
1775                 if (flg) {
1776                   PetscCall(MatHermitianTransposeGetMat(A10, &C));
1777                   PetscCall(MatHermitianTranspose(C, MAT_INITIAL_MATRIX, &B));
1778                 }
1779               }
1780               if (flg)
1781                 PetscCall(MatShellGetScalingShifts(A10, (PetscScalar *)MAT_SHELL_NOT_ALLOWED, (PetscScalar *)MAT_SHELL_NOT_ALLOWED, (Vec *)MAT_SHELL_NOT_ALLOWED, (Vec *)MAT_SHELL_NOT_ALLOWED, (Vec *)MAT_SHELL_NOT_ALLOWED, (Mat *)MAT_SHELL_NOT_ALLOWED, (IS *)MAT_SHELL_NOT_ALLOWED, (IS *)MAT_SHELL_NOT_ALLOWED));
1782               if (!B) {
1783                 B = A10;
1784                 PetscCall(PetscObjectReference((PetscObject)B));
1785               } else if (!data->is) {
1786                 PetscCall(PetscObjectTypeCompareAny((PetscObject)A01, &flg, MATTRANSPOSEVIRTUAL, MATHERMITIANTRANSPOSEVIRTUAL, ""));
1787                 if (!flg) C = A01;
1788                 else
1789                   PetscCall(MatShellGetScalingShifts(A01, (PetscScalar *)MAT_SHELL_NOT_ALLOWED, (PetscScalar *)MAT_SHELL_NOT_ALLOWED, (Vec *)MAT_SHELL_NOT_ALLOWED, (Vec *)MAT_SHELL_NOT_ALLOWED, (Vec *)MAT_SHELL_NOT_ALLOWED, (Mat *)MAT_SHELL_NOT_ALLOWED, (IS *)MAT_SHELL_NOT_ALLOWED, (IS *)MAT_SHELL_NOT_ALLOWED));
1790               }
1791               PetscCall(ISCreateStride(PETSC_COMM_SELF, B->rmap->N, 0, 1, &uis));
1792               PetscCall(ISSetIdentity(uis));
1793               if (!data->is) {
1794                 if (C) PetscCall(PetscObjectReference((PetscObject)C));
1795                 else PetscCall(MatTranspose(B, MAT_INITIAL_MATRIX, &C));
1796                 PetscCall(ISDuplicate(data_00->is, is));
1797                 PetscCall(MatIncreaseOverlap(A, 1, is, 1));
1798                 PetscCall(MatSetOption(C, MAT_SUBMAT_SINGLEIS, PETSC_TRUE));
1799                 PetscCall(MatCreateSubMatrices(C, 1, is, &uis, MAT_INITIAL_MATRIX, &sub));
1800                 PetscCall(MatDestroy(&C));
1801                 PetscCall(MatTranspose(sub[0], MAT_INITIAL_MATRIX, &C));
1802                 PetscCall(MatDestroySubMatrices(1, &sub));
1803                 PetscCall(MatFindNonzeroRows(C, &data->is));
1804                 PetscCall(MatDestroy(&C));
1805                 PetscCall(ISDestroy(is));
1806                 PetscCall(ISCreateStride(PetscObjectComm((PetscObject)data->is), A11->rmap->n, A11->rmap->rstart, 1, &loc));
1807                 if (PetscDefined(USE_DEBUG)) PetscCall(PCHPDDMCheckInclusion_Private(pc, data->is, loc, PETSC_FALSE));
1808                 PetscCall(ISExpand(data->is, loc, is));
1809                 PetscCall(ISDestroy(&loc));
1810                 PetscCall(ISDestroy(&data->is));
1811                 data->is = is[0];
1812                 is[0]    = nullptr;
1813               }
1814               if (PetscDefined(USE_DEBUG)) {
1815                 PetscCall(PCHPDDMCheckSymmetry_Private(pc, A01, A10));
1816                 PetscCall(MatCreateSubMatrices(B, 1, &uis, &data_00->is, MAT_INITIAL_MATRIX, &sub)); /* expensive check since all processes fetch all rows (but only some columns) of the constraint matrix */
1817                 PetscCall(ISDestroy(&uis));
1818                 PetscCall(ISDuplicate(data->is, &uis));
1819                 PetscCall(ISSort(uis));
1820                 PetscCall(ISComplement(uis, 0, B->rmap->N, is));
1821                 PetscCall(MatDuplicate(sub[0], MAT_COPY_VALUES, &C));
1822                 PetscCall(MatZeroRowsIS(C, is[0], 0.0, nullptr, nullptr));
1823                 PetscCall(ISDestroy(is));
1824                 PetscCall(MatMultEqual(sub[0], C, 20, &flg));
1825                 PetscCheck(flg, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "The image of A_10 (R_i^p)^T from the local primal (e.g., velocity) space to the full dual (e.g., pressure) space is not restricted to the local dual space: A_10 (R_i^p)^T != R_i^d (R_i^d)^T A_10 (R_i^p)^T"); /* cf. eq. (9) of https://hal.science/hal-02343808v6/document */
1826                 PetscCall(MatDestroy(&C));
1827                 PetscCall(MatDestroySubMatrices(1, &sub));
1828               }
1829               PetscCall(ISDestroy(&uis));
1830               PetscCall(MatDestroy(&B));
1831             }
1832             flg = PETSC_FALSE;
1833             if (!data->aux) {
1834               Mat D;
1835 
1836               PetscCall(MatCreateVecs(A11, &d, nullptr));
1837               PetscCall(MatGetDiagonal(A11, d));
1838               PetscCall(PetscObjectTypeCompareAny((PetscObject)A11, &flg, MATDIAGONAL, MATCONSTANTDIAGONAL, ""));
1839               if (!flg) {
1840                 PetscCall(MatCreateDiagonal(d, &D));
1841                 PetscCall(MatMultEqual(A11, D, 20, &flg));
1842                 PetscCall(MatDestroy(&D));
1843               }
1844               if (flg) PetscCall(PetscInfo(pc, "A11 block is likely diagonal so the PC will build an auxiliary Mat (which was not initially provided by the user)\n"));
1845             }
1846             if (data->Neumann != PETSC_BOOL3_TRUE && !flg && A11) {
1847               PetscReal norm;
1848 
1849               PetscCall(MatNorm(A11, NORM_INFINITY, &norm));
1850               PetscCheck(norm < PETSC_MACHINE_EPSILON * static_cast<PetscReal>(10.0), PetscObjectComm((PetscObject)P), PETSC_ERR_ARG_INCOMP, "-%spc_hpddm_schur_precondition geneo and -%spc_hpddm_has_neumann != true with a nonzero or non-diagonal A11 block", pcpre ? pcpre : "", pcpre ? pcpre : "");
1851               PetscCall(PetscInfo(pc, "A11 block is likely zero so the PC will build an auxiliary Mat (which was%s initially provided by the user)\n", data->aux ? "" : " not"));
1852               PetscCall(MatDestroy(&data->aux));
1853               flg = PETSC_TRUE;
1854             }
1855             if (!data->aux) { /* if A11 is near zero, e.g., Stokes equation, or diagonal, build an auxiliary (Neumann) Mat which is a (possibly slightly shifted) diagonal weighted by the inverse of the multiplicity */
1856               PetscSF            scatter;
1857               const PetscScalar *read;
1858               PetscScalar       *write, *diagonal = nullptr;
1859 
1860               PetscCall(MatDestroy(&data->aux));
1861               PetscCall(ISGetLocalSize(data->is, &n));
1862               PetscCall(VecCreateMPI(PetscObjectComm((PetscObject)P), n, PETSC_DECIDE, &xin));
1863               PetscCall(VecDuplicate(xin, &v));
1864               PetscCall(VecScatterCreate(xin, data->is, v, nullptr, &scatter));
1865               PetscCall(VecSet(v, 1.0));
1866               PetscCall(VecSet(xin, 1.0));
1867               PetscCall(VecScatterBegin(scatter, v, xin, ADD_VALUES, SCATTER_REVERSE));
1868               PetscCall(VecScatterEnd(scatter, v, xin, ADD_VALUES, SCATTER_REVERSE)); /* v has the multiplicity of all unknowns on the overlap */
1869               PetscCall(PetscSFDestroy(&scatter));
1870               if (d) {
1871                 PetscCall(VecScatterCreate(d, data->is, v, nullptr, &scatter));
1872                 PetscCall(VecScatterBegin(scatter, d, v, INSERT_VALUES, SCATTER_FORWARD));
1873                 PetscCall(VecScatterEnd(scatter, d, v, INSERT_VALUES, SCATTER_FORWARD));
1874                 PetscCall(PetscSFDestroy(&scatter));
1875                 PetscCall(VecDestroy(&d));
1876                 PetscCall(PetscMalloc1(n, &diagonal));
1877                 PetscCall(VecGetArrayRead(v, &read));
1878                 PetscCallCXX(std::copy_n(read, n, diagonal));
1879                 PetscCall(VecRestoreArrayRead(v, &read));
1880               }
1881               PetscCall(VecDestroy(&v));
1882               PetscCall(VecCreateSeq(PETSC_COMM_SELF, n, &v));
1883               PetscCall(VecGetArrayRead(xin, &read));
1884               PetscCall(VecGetArrayWrite(v, &write));
1885               for (PetscInt i = 0; i < n; ++i) write[i] = (!diagonal || std::abs(diagonal[i]) < PETSC_MACHINE_EPSILON) ? PETSC_SMALL / (static_cast<PetscReal>(1000.0) * read[i]) : diagonal[i] / read[i];
1886               PetscCall(PetscFree(diagonal));
1887               PetscCall(VecRestoreArrayRead(xin, &read));
1888               PetscCall(VecRestoreArrayWrite(v, &write));
1889               PetscCall(VecDestroy(&xin));
1890               PetscCall(MatCreateDiagonal(v, &data->aux));
1891               PetscCall(VecDestroy(&v));
1892             }
1893             uis  = data->is;
1894             uaux = data->aux;
1895             PetscCall(PetscObjectReference((PetscObject)uis));
1896             PetscCall(PetscObjectReference((PetscObject)uaux));
1897             PetscCall(PetscStrallocpy(pcpre, &prefix));
1898             PetscCall(PCSetOptionsPrefix(pc, nullptr));
1899             PetscCall(PCSetType(pc, PCKSP));                                    /* replace the PC associated to the Schur complement by PCKSP */
1900             PetscCall(KSPCreate(PetscObjectComm((PetscObject)pc), &inner_ksp)); /* new KSP that will be attached to the previously set PC */
1901             pc->ops->setup = PCSetUp_KSP;
1902             PetscCall(PetscObjectGetTabLevel((PetscObject)pc, &n));
1903             PetscCall(PetscObjectSetTabLevel((PetscObject)inner_ksp, n + 2));
1904             PetscCall(KSPSetOperators(inner_ksp, pc->mat, pc->pmat));
1905             PetscCall(KSPSetOptionsPrefix(inner_ksp, std::string(std::string(prefix) + "pc_hpddm_").c_str()));
1906             PetscCall(KSPSetSkipPCSetFromOptions(inner_ksp, PETSC_TRUE));
1907             PetscCall(KSPSetFromOptions(inner_ksp));
1908             PetscCall(KSPGetPC(inner_ksp, &inner));
1909             PetscCall(PCSetOptionsPrefix(inner, nullptr));
1910             PetscCall(PCSetType(inner, PCNONE)); /* no preconditioner since the action of M^-1 A or A M^-1 will be computed by the Amat */
1911             PetscCall(PCKSPSetKSP(pc, inner_ksp));
1912             PetscCall(PetscNew(&ctx));    /* context to pass data around for the inner-most PC, which will be a proper PCHPDDM */
1913             std::get<0>(*ctx)[0] = pc_00; /* for coarse correction on the primal (e.g., velocity) space */
1914             PetscCall(PCCreate(PetscObjectComm((PetscObject)pc), &std::get<0>(*ctx)[1]));
1915             PetscCall(PCSetOptionsPrefix(std::get<0>(*ctx)[1], prefix));
1916             PetscCall(PetscFree(prefix));
1917             PetscCall(PCSetOperators(std::get<0>(*ctx)[1], pc->mat, pc->pmat));
1918             PetscCall(PCSetType(std::get<0>(*ctx)[1], PCHPDDM));
1919             PetscCall(PCHPDDMSetAuxiliaryMat(std::get<0>(*ctx)[1], uis, uaux, nullptr, nullptr)); /* transfer ownership of the auxiliary inputs from the inner (PCKSP) to the inner-most (PCHPDDM) PC */
1920             if (flg) static_cast<PC_HPDDM *>(std::get<0>(*ctx)[1]->data)->Neumann = PETSC_BOOL3_TRUE;
1921             PetscCall(PCSetFromOptions(std::get<0>(*ctx)[1]));
1922             PetscCall(PetscObjectDereference((PetscObject)uis));
1923             PetscCall(PetscObjectDereference((PetscObject)uaux));
1924             PetscCall(MatCreateShell(PetscObjectComm((PetscObject)pc), inner->mat->rmap->n, inner->mat->cmap->n, inner->mat->rmap->N, inner->mat->cmap->N, ctx, &S)); /* MatShell computing the action of M^-1 A or A M^-1 */
1925             PetscCall(MatShellSetOperation(S, MATOP_MULT, (void (*)(void))MatMult_SchurCorrection));
1926             PetscCall(MatShellSetOperation(S, MATOP_VIEW, (void (*)(void))MatView_SchurCorrection));
1927             PetscCall(MatShellSetOperation(S, MATOP_DESTROY, (void (*)(void))MatDestroy_SchurCorrection));
1928             PetscCall(KSPGetPCSide(inner_ksp, &(std::get<2>(*ctx))));
1929             if (std::get<2>(*ctx) == PC_LEFT || std::get<2>(*ctx) == PC_SIDE_DEFAULT) {
1930               PetscCall(KSPSetPreSolve(inner_ksp, KSPPreSolve_SchurCorrection, ctx));
1931             } else { /* no support for PC_SYMMETRIC */
1932               PetscCheck(std::get<2>(*ctx) == PC_RIGHT, PetscObjectComm((PetscObject)pc), PETSC_ERR_SUP, "PCSide %s (!= %s or %s or %s)", PCSides[std::get<2>(*ctx)], PCSides[PC_SIDE_DEFAULT], PCSides[PC_LEFT], PCSides[PC_RIGHT]);
1933             }
1934             PetscCall(KSPSetPostSolve(inner_ksp, KSPPostSolve_SchurCorrection, ctx));
1935             PetscCall(PetscObjectContainerCompose((PetscObject)std::get<0>(*ctx)[1], "_PCHPDDM_Schur", ctx, nullptr));
1936             PetscCall(PCSetUp(std::get<0>(*ctx)[1]));
1937             PetscCall(KSPSetOperators(inner_ksp, S, S));
1938             PetscCall(MatCreateVecs(std::get<1>(*ctx)[0], std::get<3>(*ctx), std::get<3>(*ctx) + 1));
1939             PetscCall(VecDuplicate(std::get<3>(*ctx)[0], std::get<3>(*ctx) + 2));
1940             PetscCall(PetscObjectDereference((PetscObject)inner_ksp));
1941             PetscCall(PetscObjectDereference((PetscObject)S));
1942             for (std::vector<Vec>::iterator it = initial.begin(); it != initial.end(); ++it) PetscCall(VecDestroy(&*it));
1943             PetscFunctionReturn(PETSC_SUCCESS);
1944           } else { /* second call to PCSetUp() on the PC associated to the Schur complement, retrieve previously set context */
1945             PetscCall(PetscContainerGetPointer(container, (void **)&ctx));
1946           }
1947         }
1948       }
1949     }
1950     if (!data->is && data->N > 1) {
1951       char type[256] = {}; /* same size as in src/ksp/pc/interface/pcset.c */
1952 
1953       PetscCall(PetscObjectTypeCompareAny((PetscObject)P, &flg, MATNORMAL, MATNORMALHERMITIAN, ""));
1954       if (flg || (A->rmap->N != A->cmap->N && P->rmap->N == P->cmap->N && P->rmap->N == A->cmap->N)) {
1955         Mat B;
1956 
1957         PetscCall(PCHPDDMSetAuxiliaryMatNormal_Private(pc, A, P, &B, pcpre));
1958         if (data->correction == PC_HPDDM_COARSE_CORRECTION_DEFLATED) data->correction = PC_HPDDM_COARSE_CORRECTION_BALANCED;
1959         PetscCall(MatDestroy(&B));
1960       } else {
1961         PetscCall(PetscObjectTypeCompare((PetscObject)P, MATSCHURCOMPLEMENT, &flg));
1962         if (flg) {
1963           Mat                 A00, P00, A01, A10, A11, B, N;
1964           PCHPDDMSchurPreType type = PC_HPDDM_SCHUR_PRE_LEAST_SQUARES;
1965 
1966           PetscCall(MatSchurComplementGetSubMatrices(P, &A00, &P00, &A01, &A10, &A11));
1967           PetscCall(PetscOptionsGetEnum(nullptr, pcpre, "-pc_hpddm_schur_precondition", PCHPDDMSchurPreTypes, (PetscEnum *)&type, &flg));
1968           if (type == PC_HPDDM_SCHUR_PRE_LEAST_SQUARES) {
1969             Mat                        B01;
1970             Vec                        diagonal = nullptr;
1971             const PetscScalar         *array;
1972             MatSchurComplementAinvType type;
1973 
1974             PetscCall(PCHPDDMCheckSymmetry_Private(pc, A01, A10, &B01));
1975             if (A11) {
1976               PetscCall(MatCreateVecs(A11, &diagonal, nullptr));
1977               PetscCall(MatGetDiagonal(A11, diagonal));
1978             }
1979             PetscCall(MatCreateVecs(P00, &v, nullptr));
1980             PetscCall(MatSchurComplementGetAinvType(P, &type));
1981             PetscCheck(type == MAT_SCHUR_COMPLEMENT_AINV_DIAG || type == MAT_SCHUR_COMPLEMENT_AINV_LUMP, PetscObjectComm((PetscObject)P), PETSC_ERR_SUP, "-%smat_schur_complement_ainv_type %s", ((PetscObject)P)->prefix ? ((PetscObject)P)->prefix : "", MatSchurComplementAinvTypes[type]);
1982             if (type == MAT_SCHUR_COMPLEMENT_AINV_LUMP) {
1983               PetscCall(MatGetRowSum(P00, v));
1984               if (A00 == P00) PetscCall(PetscObjectReference((PetscObject)A00));
1985               PetscCall(MatDestroy(&P00));
1986               PetscCall(VecGetArrayRead(v, &array));
1987               PetscCall(MatCreateAIJ(PetscObjectComm((PetscObject)A00), A00->rmap->n, A00->cmap->n, A00->rmap->N, A00->cmap->N, 1, nullptr, 0, nullptr, &P00));
1988               PetscCall(MatSetOption(P00, MAT_NO_OFF_PROC_ENTRIES, PETSC_TRUE));
1989               for (n = A00->rmap->rstart; n < A00->rmap->rend; ++n) PetscCall(MatSetValue(P00, n, n, array[n - A00->rmap->rstart], INSERT_VALUES));
1990               PetscCall(MatAssemblyBegin(P00, MAT_FINAL_ASSEMBLY));
1991               PetscCall(MatAssemblyEnd(P00, MAT_FINAL_ASSEMBLY));
1992               PetscCall(VecRestoreArrayRead(v, &array));
1993               PetscCall(MatSchurComplementUpdateSubMatrices(P, A00, P00, A01, A10, A11)); /* replace P00 by diag(sum of each row of P00) */
1994               PetscCall(MatDestroy(&P00));
1995             } else PetscCall(MatGetDiagonal(P00, v));
1996             PetscCall(VecReciprocal(v)); /* inv(diag(P00))       */
1997             PetscCall(VecSqrtAbs(v));    /* sqrt(inv(diag(P00))) */
1998             PetscCall(MatDuplicate(A01, MAT_COPY_VALUES, &B));
1999             PetscCall(MatDiagonalScale(B, v, nullptr));
2000             if (B01) PetscCall(MatDiagonalScale(B01, v, nullptr));
2001             PetscCall(VecDestroy(&v));
2002             PetscCall(MatCreateNormalHermitian(B, &N));
2003             PetscCall(PCHPDDMSetAuxiliaryMatNormal_Private(pc, B, N, &P, pcpre, &diagonal, B01));
2004             PetscCall(PetscObjectTypeCompare((PetscObject)data->aux, MATSEQAIJ, &flg));
2005             if (!flg) {
2006               PetscCall(MatDestroy(&P));
2007               P = N;
2008               PetscCall(PetscObjectReference((PetscObject)P));
2009             }
2010             if (diagonal) {
2011               PetscCall(MatDiagonalSet(P, diagonal, ADD_VALUES));
2012               PetscCall(PCSetOperators(pc, P, P)); /* replace P by A01^T inv(diag(P00)) A01 - diag(P11) */
2013               PetscCall(VecDestroy(&diagonal));
2014             } else PetscCall(PCSetOperators(pc, B01 ? P : N, P));  /* replace P by A01^T inv(diag(P00)) A01                         */
2015             pc->ops->postsolve = PCPostSolve_SchurPreLeastSquares; /*  PCFIELDSPLIT expect a KSP for (P11 - A10 inv(diag(P00)) A01) */
2016             PetscCall(MatDestroy(&N));                             /*  but a PC for (A10 inv(diag(P00)) A10 - P11) is setup instead */
2017             PetscCall(MatDestroy(&P));                             /*  so the sign of the solution must be flipped                  */
2018             PetscCall(MatDestroy(&B));
2019           } else
2020             PetscCheck(type != PC_HPDDM_SCHUR_PRE_GENEO, PetscObjectComm((PetscObject)P), PETSC_ERR_ARG_INCOMP, "-%spc_hpddm_schur_precondition %s without a prior call to PCHPDDMSetAuxiliaryMat() on the A11 block%s%s", pcpre ? pcpre : "", PCHPDDMSchurPreTypes[type], pcpre ? " -" : "", pcpre ? pcpre : "");
2021           for (std::vector<Vec>::iterator it = initial.begin(); it != initial.end(); ++it) PetscCall(VecDestroy(&*it));
2022           PetscFunctionReturn(PETSC_SUCCESS);
2023         } else {
2024           PetscCall(PetscOptionsGetString(nullptr, pcpre, "-pc_hpddm_levels_1_st_pc_type", type, sizeof(type), nullptr));
2025           PetscCall(PetscStrcmp(type, PCMAT, &algebraic));
2026           PetscCheck(!algebraic || !block, PetscObjectComm((PetscObject)P), PETSC_ERR_ARG_INCOMP, "-pc_hpddm_levels_1_st_pc_type mat and -pc_hpddm_block_splitting");
2027           if (overlap != -1) {
2028             PetscCheck(!block && !algebraic, PetscObjectComm((PetscObject)P), PETSC_ERR_ARG_INCOMP, "-pc_hpddm_%s and -pc_hpddm_harmonic_overlap", block ? "block_splitting" : "levels_1_st_pc_type mat");
2029             PetscCheck(overlap >= 1, PetscObjectComm((PetscObject)P), PETSC_ERR_ARG_WRONG, "-pc_hpddm_harmonic_overlap %" PetscInt_FMT " < 1", overlap);
2030           }
2031           if (block || overlap != -1) algebraic = PETSC_TRUE;
2032           if (algebraic) {
2033             PetscCall(ISCreateStride(PETSC_COMM_SELF, P->rmap->n, P->rmap->rstart, 1, &data->is));
2034             PetscCall(MatIncreaseOverlap(P, 1, &data->is, 1));
2035             PetscCall(ISSort(data->is));
2036           } else
2037             PetscCall(PetscInfo(pc, "Cannot assemble a fully-algebraic coarse operator with an assembled Pmat and -%spc_hpddm_levels_1_st_pc_type != mat and -%spc_hpddm_block_splitting != true and -%spc_hpddm_harmonic_overlap < 1\n", pcpre ? pcpre : "", pcpre ? pcpre : "", pcpre ? pcpre : ""));
2038         }
2039       }
2040     }
2041   }
2042 #if PetscDefined(USE_DEBUG)
2043   if (data->is) PetscCall(ISDuplicate(data->is, &dis));
2044   if (data->aux) PetscCall(MatDuplicate(data->aux, MAT_COPY_VALUES, &daux));
2045 #endif
2046   if (data->is || (ismatis && data->N > 1)) {
2047     if (ismatis) {
2048       std::initializer_list<std::string> list = {MATSEQBAIJ, MATSEQSBAIJ};
2049       PetscCall(MatISGetLocalMat(P, &N));
2050       std::initializer_list<std::string>::const_iterator it = std::find(list.begin(), list.end(), ((PetscObject)N)->type_name);
2051       PetscCall(MatISRestoreLocalMat(P, &N));
2052       switch (std::distance(list.begin(), it)) {
2053       case 0:
2054         PetscCall(MatConvert(P, MATMPIBAIJ, MAT_INITIAL_MATRIX, &C));
2055         break;
2056       case 1:
2057         /* MatCreateSubMatrices() does not work with MATSBAIJ and unsorted ISes, so convert to MPIBAIJ */
2058         PetscCall(MatConvert(P, MATMPIBAIJ, MAT_INITIAL_MATRIX, &C));
2059         PetscCall(MatSetOption(C, MAT_SYMMETRIC, PETSC_TRUE));
2060         break;
2061       default:
2062         PetscCall(MatConvert(P, MATMPIAIJ, MAT_INITIAL_MATRIX, &C));
2063       }
2064       PetscCall(MatISGetLocalToGlobalMapping(P, &l2g, nullptr));
2065       PetscCall(PetscObjectReference((PetscObject)P));
2066       PetscCall(KSPSetOperators(data->levels[0]->ksp, A, C));
2067       std::swap(C, P);
2068       PetscCall(ISLocalToGlobalMappingGetSize(l2g, &n));
2069       PetscCall(ISCreateStride(PETSC_COMM_SELF, n, 0, 1, &loc));
2070       PetscCall(ISLocalToGlobalMappingApplyIS(l2g, loc, &is[0]));
2071       PetscCall(ISDestroy(&loc));
2072       /* the auxiliary Mat is _not_ the local Neumann matrix                                */
2073       /* it is the local Neumann matrix augmented (with zeros) through MatIncreaseOverlap() */
2074       data->Neumann = PETSC_BOOL3_FALSE;
2075       structure     = SAME_NONZERO_PATTERN;
2076     } else {
2077       is[0] = data->is;
2078       if (algebraic || ctx) subdomains = PETSC_TRUE;
2079       PetscCall(PetscOptionsGetBool(nullptr, pcpre, "-pc_hpddm_define_subdomains", &subdomains, nullptr));
2080       if (ctx) PetscCheck(subdomains, PetscObjectComm((PetscObject)P), PETSC_ERR_ARG_INCOMP, "-%spc_hpddm_schur_precondition geneo and -%spc_hpddm_define_subdomains false", pcpre, pcpre);
2081       if (PetscBool3ToBool(data->Neumann)) {
2082         PetscCheck(!block, PetscObjectComm((PetscObject)P), PETSC_ERR_ARG_INCOMP, "-pc_hpddm_block_splitting and -pc_hpddm_has_neumann");
2083         PetscCheck(overlap == -1, PetscObjectComm((PetscObject)P), PETSC_ERR_ARG_INCOMP, "-pc_hpddm_harmonic_overlap %" PetscInt_FMT " and -pc_hpddm_has_neumann", overlap);
2084         PetscCheck(!algebraic, PetscObjectComm((PetscObject)P), PETSC_ERR_ARG_INCOMP, "-pc_hpddm_levels_1_st_pc_type mat and -pc_hpddm_has_neumann");
2085       }
2086       if (PetscBool3ToBool(data->Neumann) || block) structure = SAME_NONZERO_PATTERN;
2087       PetscCall(ISCreateStride(PetscObjectComm((PetscObject)data->is), P->rmap->n, P->rmap->rstart, 1, &loc));
2088     }
2089     PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "%spc_hpddm_levels_1_", pcpre ? pcpre : ""));
2090     PetscCall(PetscOptionsGetEnum(nullptr, prefix, "-st_matstructure", MatStructures, (PetscEnum *)&structure, &flg)); /* if not user-provided, force its value when possible */
2091     if (!flg && structure == SAME_NONZERO_PATTERN) {                                                                   /* cannot call STSetMatStructure() yet, insert the appropriate option in the database, parsed by STSetFromOptions() */
2092       PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "-%spc_hpddm_levels_1_st_matstructure", pcpre ? pcpre : ""));
2093       PetscCall(PetscOptionsSetValue(nullptr, prefix, MatStructures[structure]));
2094     }
2095     flg = PETSC_FALSE;
2096     if (data->share) {
2097       data->share = PETSC_FALSE; /* will be reset to PETSC_TRUE if none of the conditions below are true */
2098       if (!subdomains) PetscCall(PetscInfo(pc, "Cannot share subdomain KSP between SLEPc and PETSc since -%spc_hpddm_define_subdomains is not true\n", pcpre ? pcpre : ""));
2099       else if (data->deflation) PetscCall(PetscInfo(pc, "Nothing to share since PCHPDDMSetDeflationMat() has been called\n"));
2100       else if (ismatis) PetscCall(PetscInfo(pc, "Cannot share subdomain KSP between SLEPc and PETSc with a Pmat of type MATIS\n"));
2101       else if (!algebraic && structure != SAME_NONZERO_PATTERN)
2102         PetscCall(PetscInfo(pc, "Cannot share subdomain KSP between SLEPc and PETSc since -%spc_hpddm_levels_1_st_matstructure %s (!= %s)\n", pcpre ? pcpre : "", MatStructures[structure], MatStructures[SAME_NONZERO_PATTERN]));
2103       else data->share = PETSC_TRUE;
2104     }
2105     if (!ismatis) {
2106       if (data->share || (!PetscBool3ToBool(data->Neumann) && subdomains)) PetscCall(ISDuplicate(is[0], &unsorted));
2107       else unsorted = is[0];
2108     }
2109     if (data->N > 1 && (data->aux || ismatis || algebraic)) {
2110       PetscCheck(loadedSym, PETSC_COMM_SELF, PETSC_ERR_PLIB, "HPDDM library not loaded, cannot use more than one level");
2111       PetscCall(MatSetOption(P, MAT_SUBMAT_SINGLEIS, PETSC_TRUE));
2112       if (ismatis) {
2113         /* needed by HPDDM (currently) so that the partition of unity is 0 on subdomain interfaces */
2114         PetscCall(MatIncreaseOverlap(P, 1, is, 1));
2115         PetscCall(ISDestroy(&data->is));
2116         data->is = is[0];
2117       } else {
2118         if (PetscDefined(USE_DEBUG)) PetscCall(PCHPDDMCheckInclusion_Private(pc, data->is, loc, PETSC_TRUE));
2119         if (overlap == -1) PetscCall(PetscObjectComposeFunction((PetscObject)pc->pmat, "PCHPDDMAlgebraicAuxiliaryMat_Private_C", PCHPDDMAlgebraicAuxiliaryMat_Private));
2120         if (!PetscBool3ToBool(data->Neumann) && (!algebraic || overlap != -1)) {
2121           PetscCall(PetscObjectTypeCompare((PetscObject)P, MATMPISBAIJ, &flg));
2122           if (flg) {
2123             /* maybe better to ISSort(is[0]), MatCreateSubMatrices(), and then MatPermute() */
2124             /* but there is no MatPermute_SeqSBAIJ(), so as before, just use MATMPIBAIJ     */
2125             PetscCall(MatConvert(P, MATMPIBAIJ, MAT_INITIAL_MATRIX, &uaux));
2126             flg = PETSC_FALSE;
2127           }
2128         }
2129       }
2130       if (algebraic && overlap == -1) {
2131         PetscUseMethod(pc->pmat, "PCHPDDMAlgebraicAuxiliaryMat_Private_C", (Mat, IS *, Mat *[], PetscBool), (P, is, &sub, block));
2132         if (block) {
2133           PetscCall(PetscObjectQuery((PetscObject)sub[0], "_PCHPDDM_Neumann_Mat", (PetscObject *)&data->aux));
2134           PetscCall(PetscObjectCompose((PetscObject)sub[0], "_PCHPDDM_Neumann_Mat", nullptr));
2135         }
2136       } else if (!uaux || overlap != -1) {
2137         if (!ctx) {
2138           if (PetscBool3ToBool(data->Neumann)) sub = &data->aux;
2139           else {
2140             PetscBool flg;
2141             if (overlap != -1) {
2142               Harmonic              h;
2143               Mat                   A0, *a;                    /* with an SVD: [ A_00  A_01       ] */
2144               IS                    ov[2], rows, cols, stride; /*              [ A_10  A_11  A_12 ] */
2145               const PetscInt       *i[2], bs = P->cmap->bs;    /* with a GEVP: [ A_00  A_01       ] */
2146               PetscInt              n[2];                      /*              [ A_10  A_11  A_12 ] */
2147               std::vector<PetscInt> v[2];                      /*              [       A_21  A_22 ] */
2148 
2149               PetscCall(ISDuplicate(data->is, ov));
2150               if (overlap > 1) PetscCall(MatIncreaseOverlap(P, 1, ov, overlap - 1));
2151               PetscCall(ISDuplicate(ov[0], ov + 1));
2152               PetscCall(MatIncreaseOverlap(P, 1, ov + 1, 1));
2153               PetscCall(PetscNew(&h));
2154               h->ksp = nullptr;
2155               PetscCall(PetscCalloc1(2, &h->A));
2156               PetscCall(PetscOptionsHasName(nullptr, prefix, "-svd_nsv", &flg));
2157               if (!flg) PetscCall(PetscOptionsHasName(nullptr, prefix, "-svd_relative_threshold", &flg));
2158               PetscCall(ISSort(ov[0]));
2159               if (!flg) PetscCall(ISSort(ov[1]));
2160               PetscCall(PetscCalloc1(5, &h->is));
2161               PetscCall(MatCreateSubMatrices(uaux ? uaux : P, 1, ov + !flg, ov + 1, MAT_INITIAL_MATRIX, &a)); /* submatrix from above, either square (!flg) or rectangular (flg) */
2162               for (PetscInt j = 0; j < 2; ++j) {
2163                 PetscCall(ISGetIndices(ov[j], i + j));
2164                 PetscCall(ISGetLocalSize(ov[j], n + j));
2165               }
2166               v[1].reserve((n[1] - n[0]) / bs);
2167               for (PetscInt j = 0; j < n[1]; j += bs) { /* indices of the (2,2) block */
2168                 PetscInt location;
2169                 PetscCall(ISLocate(ov[0], i[1][j], &location));
2170                 if (location < 0) v[1].emplace_back(j / bs);
2171               }
2172               if (!flg) {
2173                 h->A[1] = a[0];
2174                 PetscCall(PetscObjectReference((PetscObject)h->A[1]));
2175                 v[0].reserve((n[0] - P->rmap->n) / bs);
2176                 for (PetscInt j = 0; j < n[1]; j += bs) { /* row indices of the (1,2) block */
2177                   PetscInt location;
2178                   PetscCall(ISLocate(loc, i[1][j], &location));
2179                   if (location < 0) {
2180                     PetscCall(ISLocate(ov[0], i[1][j], &location));
2181                     if (location >= 0) v[0].emplace_back(j / bs);
2182                   }
2183                 }
2184                 PetscCall(ISCreateBlock(PETSC_COMM_SELF, bs, v[0].size(), v[0].data(), PETSC_USE_POINTER, &rows));
2185                 PetscCall(ISCreateBlock(PETSC_COMM_SELF, bs, v[1].size(), v[1].data(), PETSC_COPY_VALUES, h->is + 4));
2186                 PetscCall(MatCreateSubMatrix(a[0], rows, h->is[4], MAT_INITIAL_MATRIX, h->A)); /* A_12 submatrix from above */
2187                 PetscCall(ISDestroy(&rows));
2188                 if (uaux) PetscCall(MatConvert(a[0], MATSEQSBAIJ, MAT_INPLACE_MATRIX, a)); /* initial Pmat was MATSBAIJ, convert back to the same format since the rectangular A_12 submatrix has been created */
2189                 PetscCall(ISEmbed(ov[0], ov[1], PETSC_TRUE, &rows));
2190                 PetscCall(MatCreateSubMatrix(a[0], rows, cols = rows, MAT_INITIAL_MATRIX, &A0)); /* [ A_00  A_01 ; A_10  A_11 ] submatrix from above */
2191                 PetscCall(ISDestroy(&rows));
2192                 v[0].clear();
2193                 PetscCall(ISEmbed(loc, ov[1], PETSC_TRUE, h->is + 3));
2194                 PetscCall(ISEmbed(data->is, ov[1], PETSC_TRUE, h->is + 2));
2195               }
2196               v[0].reserve((n[0] - P->rmap->n) / bs);
2197               for (PetscInt j = 0; j < n[0]; j += bs) {
2198                 PetscInt location;
2199                 PetscCall(ISLocate(loc, i[0][j], &location));
2200                 if (location < 0) v[0].emplace_back(j / bs);
2201               }
2202               PetscCall(ISCreateBlock(PETSC_COMM_SELF, bs, v[0].size(), v[0].data(), PETSC_USE_POINTER, &rows));
2203               for (PetscInt j = 0; j < 2; ++j) PetscCall(ISRestoreIndices(ov[j], i + j));
2204               if (flg) {
2205                 IS is;
2206                 PetscCall(ISCreateStride(PETSC_COMM_SELF, a[0]->rmap->n, 0, 1, &is));
2207                 PetscCall(ISEmbed(ov[0], ov[1], PETSC_TRUE, &cols));
2208                 PetscCall(MatCreateSubMatrix(a[0], is, cols, MAT_INITIAL_MATRIX, &A0)); /* [ A_00  A_01 ; A_10  A_11 ] submatrix from above */
2209                 PetscCall(ISDestroy(&cols));
2210                 PetscCall(ISDestroy(&is));
2211                 if (uaux) PetscCall(MatConvert(A0, MATSEQSBAIJ, MAT_INPLACE_MATRIX, &A0)); /* initial Pmat was MATSBAIJ, convert back to the same format since this submatrix is square */
2212                 PetscCall(ISEmbed(loc, data->is, PETSC_TRUE, h->is + 2));
2213                 PetscCall(ISCreateBlock(PETSC_COMM_SELF, bs, v[1].size(), v[1].data(), PETSC_USE_POINTER, &cols));
2214                 PetscCall(MatCreateSubMatrix(a[0], rows, cols, MAT_INITIAL_MATRIX, h->A)); /* A_12 submatrix from above */
2215                 PetscCall(ISDestroy(&cols));
2216               }
2217               PetscCall(ISCreateStride(PETSC_COMM_SELF, A0->rmap->n, 0, 1, &stride));
2218               PetscCall(ISEmbed(rows, stride, PETSC_TRUE, h->is));
2219               PetscCall(ISDestroy(&stride));
2220               PetscCall(ISDestroy(&rows));
2221               PetscCall(ISEmbed(loc, ov[0], PETSC_TRUE, h->is + 1));
2222               if (subdomains) {
2223                 if (!data->levels[0]->pc) {
2224                   PetscCall(PCCreate(PetscObjectComm((PetscObject)pc), &data->levels[0]->pc));
2225                   PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "%spc_hpddm_levels_1_", pcpre ? pcpre : ""));
2226                   PetscCall(PCSetOptionsPrefix(data->levels[0]->pc, prefix));
2227                   PetscCall(PCSetOperators(data->levels[0]->pc, A, P));
2228                 }
2229                 PetscCall(PCSetType(data->levels[0]->pc, PCASM));
2230                 if (!data->levels[0]->pc->setupcalled) PetscCall(PCASMSetLocalSubdomains(data->levels[0]->pc, 1, ov + !flg, &loc));
2231                 PetscCall(PCHPDDMCommunicationAvoidingPCASM_Private(data->levels[0]->pc, flg ? A0 : a[0], PETSC_TRUE));
2232                 if (!flg) ++overlap;
2233                 if (data->share) {
2234                   PetscInt n = -1;
2235                   PetscTryMethod(data->levels[0]->pc, "PCASMGetSubKSP_C", (PC, PetscInt *, PetscInt *, KSP **), (data->levels[0]->pc, &n, nullptr, &ksp));
2236                   PetscCheck(n == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of subdomain solver %" PetscInt_FMT " != 1", n);
2237                   if (flg) {
2238                     h->ksp = ksp[0];
2239                     PetscCall(PetscObjectReference((PetscObject)h->ksp));
2240                   }
2241                 }
2242               }
2243               if (!h->ksp) {
2244                 PetscBool share = data->share;
2245                 PetscCall(KSPCreate(PETSC_COMM_SELF, &h->ksp));
2246                 PetscCall(KSPSetType(h->ksp, KSPPREONLY));
2247                 PetscCall(KSPSetOperators(h->ksp, A0, A0));
2248                 do {
2249                   if (!data->share) {
2250                     share = PETSC_FALSE;
2251                     PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "%spc_hpddm_levels_1_%s", pcpre ? pcpre : "", flg ? "svd_" : "eps_"));
2252                     PetscCall(KSPSetOptionsPrefix(h->ksp, prefix));
2253                     PetscCall(KSPSetFromOptions(h->ksp));
2254                   } else {
2255                     MatSolverType type;
2256                     PetscCall(KSPGetPC(ksp[0], &pc));
2257                     PetscCall(PetscObjectTypeCompareAny((PetscObject)pc, &data->share, PCLU, PCCHOLESKY, ""));
2258                     if (data->share) {
2259                       PetscCall(PCFactorGetMatSolverType(pc, &type));
2260                       if (!type) {
2261                         if (PetscDefined(HAVE_MUMPS)) PetscCall(PCFactorSetMatSolverType(pc, MATSOLVERMUMPS));
2262                         else if (PetscDefined(HAVE_MKL_PARDISO)) PetscCall(PCFactorSetMatSolverType(pc, MATSOLVERMKL_PARDISO));
2263                         else data->share = PETSC_FALSE;
2264                         if (data->share) PetscCall(PCSetFromOptions(pc));
2265                       } else {
2266                         PetscCall(PetscStrcmp(type, MATSOLVERMUMPS, &data->share));
2267                         if (!data->share) PetscCall(PetscStrcmp(type, MATSOLVERMKL_PARDISO, &data->share));
2268                       }
2269                       if (data->share) {
2270                         std::tuple<KSP, IS, Vec[2]> *p;
2271                         PetscCall(PCFactorGetMatrix(pc, &A));
2272                         PetscCall(MatFactorSetSchurIS(A, h->is[4]));
2273                         PetscCall(KSPSetUp(ksp[0]));
2274                         PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "%spc_hpddm_levels_1_eps_shell_", pcpre ? pcpre : ""));
2275                         PetscCall(KSPSetOptionsPrefix(h->ksp, prefix));
2276                         PetscCall(KSPSetFromOptions(h->ksp));
2277                         PetscCall(KSPGetPC(h->ksp, &pc));
2278                         PetscCall(PCSetType(pc, PCSHELL));
2279                         PetscCall(PetscNew(&p));
2280                         std::get<0>(*p) = ksp[0];
2281                         PetscCall(ISEmbed(ov[0], ov[1], PETSC_TRUE, &std::get<1>(*p)));
2282                         PetscCall(MatCreateVecs(A, std::get<2>(*p), std::get<2>(*p) + 1));
2283                         PetscCall(PCShellSetContext(pc, p));
2284                         PetscCall(PCShellSetApply(pc, PCApply_Schur));
2285                         PetscCall(PCShellSetApplyTranspose(pc, PCApply_Schur<Vec, true>));
2286                         PetscCall(PCShellSetMatApply(pc, PCApply_Schur<Mat>));
2287                         PetscCall(PCShellSetDestroy(pc, PCDestroy_Schur));
2288                       }
2289                     }
2290                     if (!data->share) PetscCall(PetscInfo(pc, "Cannot share subdomain KSP between SLEPc and PETSc since neither MUMPS nor MKL PARDISO is used\n"));
2291                   }
2292                 } while (!share != !data->share); /* if data->share is initially PETSC_TRUE, but then reset to PETSC_FALSE, then go back to the beginning of the do loop */
2293               }
2294               PetscCall(ISDestroy(ov));
2295               PetscCall(ISDestroy(ov + 1));
2296               if (overlap == 1 && subdomains && flg) {
2297                 *subA = A0;
2298                 sub   = subA;
2299                 if (uaux) PetscCall(MatDestroy(&uaux));
2300               } else PetscCall(MatDestroy(&A0));
2301               PetscCall(MatCreateShell(PETSC_COMM_SELF, P->rmap->n, n[1] - n[0], P->rmap->n, n[1] - n[0], h, &data->aux));
2302               PetscCall(KSPSetErrorIfNotConverged(h->ksp, PETSC_TRUE)); /* bail out as early as possible to avoid (apparently) unrelated error messages */
2303               PetscCall(MatCreateVecs(h->ksp->pc->pmat, &h->v, nullptr));
2304               PetscCall(MatShellSetOperation(data->aux, MATOP_MULT, (void (*)(void))MatMult_Harmonic));
2305               PetscCall(MatShellSetOperation(data->aux, MATOP_MULT_TRANSPOSE, (void (*)(void))MatMultTranspose_Harmonic));
2306               PetscCall(MatShellSetMatProductOperation(data->aux, MATPRODUCT_AB, nullptr, MatProduct_AB_Harmonic, nullptr, MATDENSE, MATDENSE));
2307               PetscCall(MatShellSetOperation(data->aux, MATOP_DESTROY, (void (*)(void))MatDestroy_Harmonic));
2308               PetscCall(MatDestroySubMatrices(1, &a));
2309             }
2310             if (overlap != 1 || !subdomains) {
2311               PetscCall(MatCreateSubMatrices(uaux ? uaux : P, 1, is, is, MAT_INITIAL_MATRIX, &sub));
2312               if (ismatis) {
2313                 PetscCall(MatISGetLocalMat(C, &N));
2314                 PetscCall(PetscObjectTypeCompare((PetscObject)N, MATSEQSBAIJ, &flg));
2315                 if (flg) PetscCall(MatConvert(sub[0], MATSEQSBAIJ, MAT_INPLACE_MATRIX, sub));
2316                 PetscCall(MatISRestoreLocalMat(C, &N));
2317               }
2318             }
2319             if (uaux) {
2320               PetscCall(MatDestroy(&uaux));
2321               PetscCall(MatConvert(sub[0], MATSEQSBAIJ, MAT_INPLACE_MATRIX, sub));
2322             }
2323           }
2324         }
2325       } else {
2326         PetscCall(MatCreateSubMatrices(uaux, 1, is, is, MAT_INITIAL_MATRIX, &sub));
2327         PetscCall(MatDestroy(&uaux));
2328         PetscCall(MatConvert(sub[0], MATSEQSBAIJ, MAT_INPLACE_MATRIX, sub));
2329       }
2330       /* Vec holding the partition of unity */
2331       if (!data->levels[0]->D) {
2332         PetscCall(ISGetLocalSize(data->is, &n));
2333         PetscCall(VecCreateMPI(PETSC_COMM_SELF, n, PETSC_DETERMINE, &data->levels[0]->D));
2334       }
2335       if (data->share && overlap == -1) {
2336         Mat      D;
2337         IS       perm = nullptr;
2338         PetscInt size = -1;
2339 
2340         if (!data->levels[0]->pc) {
2341           PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "%spc_hpddm_levels_1_", pcpre ? pcpre : ""));
2342           PetscCall(PCCreate(PetscObjectComm((PetscObject)pc), &data->levels[0]->pc));
2343           PetscCall(PCSetOptionsPrefix(data->levels[0]->pc, prefix));
2344           PetscCall(PCSetOperators(data->levels[0]->pc, A, P));
2345         }
2346         PetscCall(PCSetType(data->levels[0]->pc, PCASM));
2347         if (!ctx) {
2348           if (!data->levels[0]->pc->setupcalled) {
2349             IS sorted; /* PCASM will sort the input IS, duplicate it to return an unmodified (PCHPDDM) input IS */
2350             PetscCall(ISDuplicate(is[0], &sorted));
2351             PetscCall(PCASMSetLocalSubdomains(data->levels[0]->pc, 1, &sorted, &loc));
2352             PetscCall(PetscObjectDereference((PetscObject)sorted));
2353           }
2354           PetscCall(PCSetFromOptions(data->levels[0]->pc));
2355           if (block) {
2356             PetscCall(PCHPDDMPermute_Private(unsorted, data->is, &uis, sub[0], &C, &perm));
2357             PetscCall(PCHPDDMCommunicationAvoidingPCASM_Private(data->levels[0]->pc, C, algebraic));
2358           } else PetscCall(PCSetUp(data->levels[0]->pc));
2359           PetscTryMethod(data->levels[0]->pc, "PCASMGetSubKSP_C", (PC, PetscInt *, PetscInt *, KSP **), (data->levels[0]->pc, &size, nullptr, &ksp));
2360           if (size != 1) {
2361             data->share = PETSC_FALSE;
2362             PetscCheck(size == -1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of subdomain solver %" PetscInt_FMT " != 1", size);
2363             PetscCall(PetscInfo(pc, "Cannot share subdomain KSP between SLEPc and PETSc since PCASMGetSubKSP() not found in fine-level PC\n"));
2364             PetscCall(ISDestroy(&unsorted));
2365             unsorted = is[0];
2366           } else {
2367             const char *matpre;
2368             PetscBool   cmp[4];
2369 
2370             if (!block && !ctx) PetscCall(PCHPDDMPermute_Private(unsorted, data->is, &uis, PetscBool3ToBool(data->Neumann) ? sub[0] : data->aux, &C, &perm));
2371             if (!PetscBool3ToBool(data->Neumann) && !block) {
2372               PetscCall(MatPermute(sub[0], perm, perm, &D)); /* permute since PCASM will call ISSort() */
2373               PetscCall(MatHeaderReplace(sub[0], &D));
2374             }
2375             if (data->B) { /* see PCHPDDMSetRHSMat() */
2376               PetscCall(MatPermute(data->B, perm, perm, &D));
2377               PetscCall(MatHeaderReplace(data->B, &D));
2378             }
2379             PetscCall(ISDestroy(&perm));
2380             PetscCall(KSPGetOperators(ksp[0], subA, subA + 1));
2381             PetscCall(PetscObjectReference((PetscObject)subA[0]));
2382             PetscCall(MatDuplicate(subA[1], MAT_SHARE_NONZERO_PATTERN, &D));
2383             PetscCall(MatGetOptionsPrefix(subA[1], &matpre));
2384             PetscCall(MatSetOptionsPrefix(D, matpre));
2385             PetscCall(PetscObjectTypeCompare((PetscObject)D, MATNORMAL, cmp));
2386             PetscCall(PetscObjectTypeCompare((PetscObject)C, MATNORMAL, cmp + 1));
2387             if (!cmp[0]) PetscCall(PetscObjectTypeCompare((PetscObject)D, MATNORMALHERMITIAN, cmp + 2));
2388             else cmp[2] = PETSC_FALSE;
2389             if (!cmp[1]) PetscCall(PetscObjectTypeCompare((PetscObject)C, MATNORMALHERMITIAN, cmp + 3));
2390             else cmp[3] = PETSC_FALSE;
2391             PetscCheck(cmp[0] == cmp[1] && cmp[2] == cmp[3], PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "-%spc_hpddm_levels_1_pc_asm_sub_mat_type %s and auxiliary Mat of type %s", pcpre ? pcpre : "", ((PetscObject)D)->type_name, ((PetscObject)C)->type_name);
2392             if (!cmp[0] && !cmp[2]) {
2393               if (!block) PetscCall(MatAXPY(D, 1.0, C, SUBSET_NONZERO_PATTERN));
2394               else {
2395                 PetscCall(MatMissingDiagonal(D, cmp, nullptr));
2396                 if (cmp[0]) structure = DIFFERENT_NONZERO_PATTERN; /* data->aux has no missing diagonal entry */
2397                 PetscCall(MatAXPY(D, 1.0, data->aux, structure));
2398               }
2399             } else {
2400               Mat mat[2];
2401 
2402               if (cmp[0]) {
2403                 PetscCall(MatNormalGetMat(D, mat));
2404                 PetscCall(MatNormalGetMat(C, mat + 1));
2405               } else {
2406                 PetscCall(MatNormalHermitianGetMat(D, mat));
2407                 PetscCall(MatNormalHermitianGetMat(C, mat + 1));
2408               }
2409               PetscCall(MatAXPY(mat[0], 1.0, mat[1], SUBSET_NONZERO_PATTERN));
2410             }
2411             PetscCall(MatPropagateSymmetryOptions(C, D));
2412             PetscCall(MatDestroy(&C));
2413             C = D;
2414             /* swap pointers so that variables stay consistent throughout PCSetUp() */
2415             std::swap(C, data->aux);
2416             std::swap(uis, data->is);
2417             swap = PETSC_TRUE;
2418           }
2419         }
2420       }
2421       if (ctx) {
2422         PC_HPDDM              *data_00 = (PC_HPDDM *)std::get<0>(*ctx)[0]->data;
2423         PC                     s;
2424         Mat                    A00, P00, A01 = nullptr, A10, A11, N, b[4];
2425         IS                     sorted, is[2], *is_00;
2426         MatSolverType          type;
2427         std::pair<PC, Vec[2]> *p;
2428 
2429         n = -1;
2430         PetscTryMethod(data_00->levels[0]->pc, "PCASMGetSubKSP_C", (PC, PetscInt *, PetscInt *, KSP **), (data_00->levels[0]->pc, &n, nullptr, &ksp));
2431         PetscCheck(n == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of subdomain solver %" PetscInt_FMT " != 1", n);
2432         PetscCall(KSPGetOperators(ksp[0], subA, subA + 1));
2433         PetscCall(ISGetLocalSize(data_00->is, &n));
2434         if (n != subA[0]->rmap->n || n != subA[0]->cmap->n) {
2435           PetscCall(PCASMGetLocalSubdomains(data_00->levels[0]->pc, &n, &is_00, nullptr));
2436           PetscCall(ISGetLocalSize(*is_00, &n));
2437           PetscCheck(n == subA[0]->rmap->n && n == subA[0]->cmap->n, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "-%spc_hpddm_schur_precondition geneo and -%spc_hpddm_define_subdomains false", pcpre ? pcpre : "", ((PetscObject)pc)->prefix);
2438         } else is_00 = &data_00->is;
2439         PetscCall(PCHPDDMPermute_Private(unsorted, data->is, &uis, data->aux, &C, nullptr)); /* permute since PCASM works with a sorted IS */
2440         std::swap(C, data->aux);
2441         std::swap(uis, data->is);
2442         swap = PETSC_TRUE;
2443         PetscCall(PCASMSetType(data->levels[0]->pc, PC_ASM_NONE)); /* "Neumann--Neumann" preconditioning with overlap and a Boolean partition of unity */
2444         PetscCall(PCASMSetLocalSubdomains(data->levels[0]->pc, 1, &data->is, &loc));
2445         PetscCall(PCSetFromOptions(data->levels[0]->pc)); /* action of eq. (15) of https://hal.science/hal-02343808v6/document (with a sign flip) */
2446         PetscCall(MatSchurComplementGetSubMatrices(P, &A00, &P00, std::get<1>(*ctx), &A10, &A11));
2447         std::get<1>(*ctx)[1] = A10;
2448         PetscCall(PetscObjectTypeCompare((PetscObject)A10, MATTRANSPOSEVIRTUAL, &flg));
2449         if (flg) PetscCall(MatTransposeGetMat(A10, &A01));
2450         else {
2451           PetscBool flg;
2452 
2453           PetscCall(PetscObjectTypeCompare((PetscObject)A10, MATHERMITIANTRANSPOSEVIRTUAL, &flg));
2454           if (flg) PetscCall(MatHermitianTransposeGetMat(A10, &A01));
2455         }
2456         PetscCall(ISDuplicate(*is_00, &sorted)); /* during setup of the PC associated to the A00 block, this IS has already been sorted, but it's put back to its original state at the end of PCSetUp_HPDDM(), which may be unsorted */
2457         PetscCall(ISSort(sorted));               /* this is to avoid changing users inputs, but it requires a new call to ISSort() here                                                                                               */
2458         if (!A01) {
2459           PetscCall(MatSetOption(A10, MAT_SUBMAT_SINGLEIS, PETSC_TRUE));
2460           PetscCall(MatCreateSubMatrices(A10, 1, &data->is, &sorted, MAT_INITIAL_MATRIX, &sub));
2461           b[2] = sub[0];
2462           PetscCall(PetscObjectReference((PetscObject)sub[0]));
2463           PetscCall(MatDestroySubMatrices(1, &sub));
2464           PetscCall(PetscObjectTypeCompare((PetscObject)std::get<1>(*ctx)[0], MATTRANSPOSEVIRTUAL, &flg));
2465           A10 = nullptr;
2466           if (flg) PetscCall(MatTransposeGetMat(std::get<1>(*ctx)[0], &A10));
2467           else {
2468             PetscBool flg;
2469 
2470             PetscCall(PetscObjectTypeCompare((PetscObject)std::get<1>(*ctx)[0], MATHERMITIANTRANSPOSEVIRTUAL, &flg));
2471             if (flg) PetscCall(MatHermitianTransposeGetMat(std::get<1>(*ctx)[0], &A10));
2472           }
2473           if (!A10) PetscCall(MatCreateSubMatrices(std::get<1>(*ctx)[0], 1, &sorted, &data->is, MAT_INITIAL_MATRIX, &sub));
2474           else {
2475             if (flg) PetscCall(MatCreateTranspose(b[2], b + 1));
2476             else PetscCall(MatCreateHermitianTranspose(b[2], b + 1));
2477           }
2478         } else {
2479           PetscCall(MatSetOption(A01, MAT_SUBMAT_SINGLEIS, PETSC_TRUE));
2480           PetscCall(MatCreateSubMatrices(A01, 1, &sorted, &data->is, MAT_INITIAL_MATRIX, &sub));
2481           if (flg) PetscCall(MatCreateTranspose(*sub, b + 2));
2482           else PetscCall(MatCreateHermitianTranspose(*sub, b + 2));
2483         }
2484         if (A01 || !A10) {
2485           b[1] = sub[0];
2486           PetscCall(PetscObjectReference((PetscObject)sub[0]));
2487         }
2488         PetscCall(MatDestroySubMatrices(1, &sub));
2489         PetscCall(ISDestroy(&sorted));
2490         PetscCall(MatCreateSchurComplement(subA[0], subA[1], b[1], b[2], data->aux, &S));
2491         PetscCall(MatSchurComplementSetKSP(S, ksp[0]));
2492         PetscCall(PCHPDDMCommunicationAvoidingPCASM_Private(data->levels[0]->pc, S, PETSC_TRUE)); /* the subdomain Mat is already known and the input IS of PCASMSetLocalSubdomains() is already sorted */
2493         PetscTryMethod(data->levels[0]->pc, "PCASMGetSubKSP_C", (PC, PetscInt *, PetscInt *, KSP **), (data->levels[0]->pc, &n, nullptr, &ksp));
2494         PetscCheck(n == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of subdomain solver %" PetscInt_FMT " != 1", n);
2495         PetscCall(KSPGetPC(ksp[0], &inner));
2496         PetscCall(PCSetType(inner, PCSHELL)); /* compute the action of the inverse of the local Schur complement with a PCSHELL */
2497         b[0] = subA[0];
2498         b[3] = data->aux;
2499         PetscCall(MatCreateNest(PETSC_COMM_SELF, 2, nullptr, 2, nullptr, b, &N)); /* instead of computing inv(A11 - A10 inv(A00) A01), compute inv([A00, A01; A10, A11]) followed by a partial solution associated to the A11 block */
2500         PetscCall(PetscObjectDereference((PetscObject)b[1]));
2501         PetscCall(PetscObjectDereference((PetscObject)b[2]));
2502         PetscCall(PCCreate(PETSC_COMM_SELF, &s));
2503         PetscCall(PCSetOptionsPrefix(s, ((PetscObject)inner)->prefix));
2504         PetscCall(PCSetOptionsPrefix(inner, nullptr));
2505         PetscCall(KSPSetSkipPCSetFromOptions(ksp[0], PETSC_TRUE));
2506         PetscCall(PCSetType(s, PCLU));
2507         if (PetscDefined(HAVE_MUMPS)) { /* only MATSOLVERMUMPS handles MATNEST, so for the others, e.g., MATSOLVERPETSC or MATSOLVERMKL_PARDISO, convert to plain MATAIJ */
2508           PetscCall(PCFactorSetMatSolverType(s, MATSOLVERMUMPS));
2509         }
2510         PetscCall(PCSetFromOptions(s));
2511         PetscCall(PCFactorGetMatSolverType(s, &type));
2512         PetscCall(PetscStrcmp(type, MATSOLVERMUMPS, &flg));
2513         if (flg) {
2514           PetscCall(PCSetOperators(s, N, N));
2515           PetscCall(PCFactorGetMatrix(s, b));
2516           PetscCall(MatSetOptionsPrefix(*b, ((PetscObject)s)->prefix));
2517           n = -1;
2518           PetscCall(PetscOptionsGetInt(nullptr, ((PetscObject)s)->prefix, "-mat_mumps_icntl_26", &n, nullptr));
2519           if (n == 1) {                                /* allocates a square MatDense of size is[1]->map->n, so one */
2520             PetscCall(MatNestGetISs(N, is, nullptr));  /*  needs to be able to deactivate this path when dealing    */
2521             PetscCall(MatFactorSetSchurIS(*b, is[1])); /*  with a large constraint space in order to avoid OOM      */
2522           }
2523         } else {
2524           PetscCall(MatConvert(N, MATAIJ, MAT_INITIAL_MATRIX, b));
2525           PetscCall(PCSetOperators(s, N, *b));
2526           PetscCall(PetscObjectDereference((PetscObject)*b));
2527           PetscCall(PCFactorGetMatrix(s, b)); /* MATSOLVERMKL_PARDISO cannot compute in PETSc (yet) a partial solution associated to the A11 block, only partial solution associated to the A00 block or full solution */
2528         }
2529         PetscCall(PetscNew(&p));
2530         p->first = s;
2531         PetscCall(MatCreateVecs(*b, p->second, p->second + 1));
2532         PetscCall(PCShellSetContext(inner, p));
2533         PetscCall(PCShellSetApply(inner, PCApply_Nest));
2534         PetscCall(PCShellSetView(inner, PCView_Nest));
2535         PetscCall(PCShellSetDestroy(inner, PCDestroy_Nest));
2536         PetscCall(PetscObjectDereference((PetscObject)N));
2537       }
2538       if (!data->levels[0]->scatter) {
2539         PetscCall(MatCreateVecs(P, &xin, nullptr));
2540         if (ismatis) PetscCall(MatDestroy(&P));
2541         PetscCall(VecScatterCreate(xin, data->is, data->levels[0]->D, nullptr, &data->levels[0]->scatter));
2542         PetscCall(VecDestroy(&xin));
2543       }
2544       if (data->levels[0]->P) {
2545         /* if the pattern is the same and PCSetUp() has previously succeeded, reuse HPDDM buffers and connectivity */
2546         PetscCall(HPDDM::Schwarz<PetscScalar>::destroy(data->levels[0], pc->setupcalled < 1 || pc->flag == DIFFERENT_NONZERO_PATTERN ? PETSC_TRUE : PETSC_FALSE));
2547       }
2548       if (!data->levels[0]->P) data->levels[0]->P = new HPDDM::Schwarz<PetscScalar>();
2549       if (data->log_separate) PetscCall(PetscLogEventBegin(PC_HPDDM_SetUp[0], data->levels[0]->ksp, nullptr, nullptr, nullptr));
2550       else PetscCall(PetscLogEventBegin(PC_HPDDM_Strc, data->levels[0]->ksp, nullptr, nullptr, nullptr));
2551       /* HPDDM internal data structure */
2552       PetscCall(data->levels[0]->P->structure(loc, data->is, !ctx ? sub[0] : nullptr, ismatis ? C : data->aux, data->levels));
2553       if (!data->log_separate) PetscCall(PetscLogEventEnd(PC_HPDDM_Strc, data->levels[0]->ksp, nullptr, nullptr, nullptr));
2554       /* matrix pencil of the generalized eigenvalue problem on the overlap (GenEO) */
2555       if (!ctx) {
2556         if (data->deflation || overlap != -1) weighted = data->aux;
2557         else if (!data->B) {
2558           PetscBool cmp;
2559 
2560           PetscCall(MatDuplicate(sub[0], MAT_COPY_VALUES, &weighted));
2561           PetscCall(PetscObjectTypeCompareAny((PetscObject)weighted, &cmp, MATNORMAL, MATNORMALHERMITIAN, ""));
2562           if (cmp) flg = PETSC_FALSE;
2563           PetscCall(MatDiagonalScale(weighted, data->levels[0]->D, data->levels[0]->D));
2564           /* neither MatDuplicate() nor MatDiagonalScale() handles the symmetry options, so propagate the options explicitly */
2565           /* only useful for -mat_type baij -pc_hpddm_levels_1_st_pc_type cholesky (no problem with MATAIJ or MATSBAIJ)      */
2566           PetscCall(MatPropagateSymmetryOptions(sub[0], weighted));
2567         } else weighted = data->B;
2568       } else weighted = nullptr;
2569       /* SLEPc is used inside the loaded symbol */
2570       PetscCall((*loadedSym)(data->levels[0]->P, data->is, ismatis ? C : (algebraic && !block && overlap == -1 ? sub[0] : (!ctx ? data->aux : S)), weighted, data->B, initial, data->levels));
2571       if (!ctx && data->share && overlap == -1) {
2572         Mat st[2];
2573         PetscCall(KSPGetOperators(ksp[0], st, st + 1));
2574         PetscCall(MatCopy(subA[0], st[0], structure));
2575         if (subA[1] != subA[0] || st[1] != st[0]) PetscCall(MatCopy(subA[1], st[1], SAME_NONZERO_PATTERN));
2576         PetscCall(PetscObjectDereference((PetscObject)subA[0]));
2577       }
2578       if (data->log_separate) PetscCall(PetscLogEventEnd(PC_HPDDM_SetUp[0], data->levels[0]->ksp, nullptr, nullptr, nullptr));
2579       if (ismatis) PetscCall(MatISGetLocalMat(C, &N));
2580       else N = data->aux;
2581       if (!ctx) P = sub[0];
2582       else P = S;
2583       /* going through the grid hierarchy */
2584       for (n = 1; n < data->N; ++n) {
2585         if (data->log_separate) PetscCall(PetscLogEventBegin(PC_HPDDM_SetUp[n], data->levels[n]->ksp, nullptr, nullptr, nullptr));
2586         /* method composed in the loaded symbol since there, SLEPc is used as well */
2587         PetscTryMethod(data->levels[0]->ksp, "PCHPDDMSetUp_Private_C", (Mat *, Mat *, PetscInt, PetscInt *const, PC_HPDDM_Level **const), (&P, &N, n, &data->N, data->levels));
2588         if (data->log_separate) PetscCall(PetscLogEventEnd(PC_HPDDM_SetUp[n], data->levels[n]->ksp, nullptr, nullptr, nullptr));
2589       }
2590       /* reset to NULL to avoid any faulty use */
2591       PetscCall(PetscObjectComposeFunction((PetscObject)data->levels[0]->ksp, "PCHPDDMSetUp_Private_C", nullptr));
2592       if (!ismatis) PetscCall(PetscObjectComposeFunction((PetscObject)pc->pmat, "PCHPDDMAlgebraicAuxiliaryMat_C", nullptr));
2593       else PetscCall(PetscObjectDereference((PetscObject)C)); /* matching PetscObjectReference() above */
2594       for (n = 0; n < data->N - 1; ++n)
2595         if (data->levels[n]->P) {
2596           /* HPDDM internal work buffers */
2597           data->levels[n]->P->setBuffer();
2598           data->levels[n]->P->super::start();
2599         }
2600       if (ismatis || !subdomains) PetscCall(PCHPDDMDestroySubMatrices_Private(PetscBool3ToBool(data->Neumann), PetscBool(algebraic && !block && overlap == -1), sub));
2601       if (ismatis) data->is = nullptr;
2602       for (n = 0; n < data->N - 1 + (reused > 0); ++n) {
2603         if (data->levels[n]->P) {
2604           PC spc;
2605 
2606           /* force the PC to be PCSHELL to do the coarse grid corrections */
2607           PetscCall(KSPSetSkipPCSetFromOptions(data->levels[n]->ksp, PETSC_TRUE));
2608           PetscCall(KSPGetPC(data->levels[n]->ksp, &spc));
2609           PetscCall(PCSetType(spc, PCSHELL));
2610           PetscCall(PCShellSetContext(spc, data->levels[n]));
2611           PetscCall(PCShellSetSetUp(spc, PCSetUp_HPDDMShell));
2612           PetscCall(PCShellSetApply(spc, PCApply_HPDDMShell));
2613           PetscCall(PCShellSetMatApply(spc, PCMatApply_HPDDMShell));
2614           if (ctx && n == 0) {
2615             Mat                               Amat, Pmat;
2616             PetscInt                          m, M;
2617             std::tuple<Mat, PetscSF, Vec[2]> *ctx;
2618 
2619             PetscCall(KSPGetOperators(data->levels[n]->ksp, nullptr, &Pmat));
2620             PetscCall(MatGetLocalSize(Pmat, &m, nullptr));
2621             PetscCall(MatGetSize(Pmat, &M, nullptr));
2622             PetscCall(PetscNew(&ctx));
2623             std::get<0>(*ctx) = S;
2624             std::get<1>(*ctx) = data->levels[n]->scatter;
2625             PetscCall(MatCreateShell(PetscObjectComm((PetscObject)data->levels[n]->ksp), m, m, M, M, ctx, &Amat));
2626             PetscCall(MatShellSetOperation(Amat, MATOP_MULT, (void (*)(void))MatMult_Schur<false>));
2627             PetscCall(MatShellSetOperation(Amat, MATOP_MULT_TRANSPOSE, (void (*)(void))MatMult_Schur<true>));
2628             PetscCall(MatShellSetOperation(Amat, MATOP_DESTROY, (void (*)(void))MatDestroy_Schur));
2629             PetscCall(MatCreateVecs(S, std::get<2>(*ctx), std::get<2>(*ctx) + 1));
2630             PetscCall(KSPSetOperators(data->levels[n]->ksp, Amat, Pmat));
2631             PetscCall(PetscObjectDereference((PetscObject)Amat));
2632           }
2633           PetscCall(PCShellSetDestroy(spc, PCDestroy_HPDDMShell));
2634           if (!data->levels[n]->pc) PetscCall(PCCreate(PetscObjectComm((PetscObject)data->levels[n]->ksp), &data->levels[n]->pc));
2635           if (n < reused) {
2636             PetscCall(PCSetReusePreconditioner(spc, PETSC_TRUE));
2637             PetscCall(PCSetReusePreconditioner(data->levels[n]->pc, PETSC_TRUE));
2638           }
2639           PetscCall(PCSetUp(spc));
2640         }
2641       }
2642       if (ctx) PetscCall(MatDestroy(&S));
2643       if (overlap == -1) PetscCall(PetscObjectComposeFunction((PetscObject)pc->pmat, "PCHPDDMAlgebraicAuxiliaryMat_Private_C", nullptr));
2644     } else flg = reused ? PETSC_FALSE : PETSC_TRUE;
2645     if (!ismatis && subdomains) {
2646       if (flg) PetscCall(KSPGetPC(data->levels[0]->ksp, &inner));
2647       else inner = data->levels[0]->pc;
2648       if (inner) {
2649         if (!inner->setupcalled) PetscCall(PCSetType(inner, PCASM));
2650         PetscCall(PCSetFromOptions(inner));
2651         PetscCall(PetscStrcmp(((PetscObject)inner)->type_name, PCASM, &flg));
2652         if (flg) {
2653           if (!inner->setupcalled) { /* evaluates to PETSC_FALSE when -pc_hpddm_block_splitting */
2654             IS sorted;               /* PCASM will sort the input IS, duplicate it to return an unmodified (PCHPDDM) input IS */
2655 
2656             PetscCall(ISDuplicate(is[0], &sorted));
2657             PetscCall(PCASMSetLocalSubdomains(inner, 1, &sorted, &loc));
2658             PetscCall(PetscObjectDereference((PetscObject)sorted));
2659           }
2660           if (!PetscBool3ToBool(data->Neumann) && data->N > 1) { /* subdomain matrices are already created for the eigenproblem, reuse them for the fine-level PC */
2661             PetscCall(PCHPDDMPermute_Private(*is, nullptr, nullptr, sub[0], &P, nullptr));
2662             PetscCall(PCHPDDMCommunicationAvoidingPCASM_Private(inner, P, algebraic));
2663             PetscCall(PetscObjectDereference((PetscObject)P));
2664           }
2665         }
2666       }
2667       if (data->N > 1) {
2668         if (overlap != 1) PetscCall(PCHPDDMDestroySubMatrices_Private(PetscBool3ToBool(data->Neumann), PetscBool(algebraic && !block && overlap == -1), sub));
2669         if (overlap == 1) PetscCall(MatDestroy(subA));
2670       }
2671     }
2672     PetscCall(ISDestroy(&loc));
2673   } else data->N = 1 + reused; /* enforce this value to 1 + reused if there is no way to build another level */
2674   if (requested != data->N + reused) {
2675     PetscCall(PetscInfo(pc, "%" PetscInt_FMT " levels requested, only %" PetscInt_FMT " built + %" PetscInt_FMT " reused. Options for level(s) > %" PetscInt_FMT ", including -%spc_hpddm_coarse_ will not be taken into account\n", requested, data->N, reused,
2676                         data->N, pcpre ? pcpre : ""));
2677     PetscCall(PetscInfo(pc, "It is best to tune parameters, e.g., a higher value for -%spc_hpddm_levels_%" PetscInt_FMT "_eps_threshold so that at least one local deflation vector will be selected\n", pcpre ? pcpre : "", data->N));
2678     /* cannot use PCDestroy_HPDDMShell() because PCSHELL not set for unassembled levels */
2679     for (n = data->N - 1; n < requested - 1; ++n) {
2680       if (data->levels[n]->P) {
2681         PetscCall(HPDDM::Schwarz<PetscScalar>::destroy(data->levels[n], PETSC_TRUE));
2682         PetscCall(VecDestroyVecs(1, &data->levels[n]->v[0]));
2683         PetscCall(VecDestroyVecs(2, &data->levels[n]->v[1]));
2684         PetscCall(MatDestroy(data->levels[n]->V));
2685         PetscCall(MatDestroy(data->levels[n]->V + 1));
2686         PetscCall(MatDestroy(data->levels[n]->V + 2));
2687         PetscCall(VecDestroy(&data->levels[n]->D));
2688         PetscCall(PetscSFDestroy(&data->levels[n]->scatter));
2689       }
2690     }
2691     if (reused) {
2692       for (n = reused; n < PETSC_PCHPDDM_MAXLEVELS && data->levels[n]; ++n) {
2693         PetscCall(KSPDestroy(&data->levels[n]->ksp));
2694         PetscCall(PCDestroy(&data->levels[n]->pc));
2695       }
2696     }
2697     PetscCheck(!PetscDefined(USE_DEBUG), PetscObjectComm((PetscObject)pc), PETSC_ERR_ARG_WRONG, "%" PetscInt_FMT " levels requested, only %" PetscInt_FMT " built + %" PetscInt_FMT " reused. Options for level(s) > %" PetscInt_FMT ", including -%spc_hpddm_coarse_ will not be taken into account. It is best to tune parameters, e.g., a higher value for -%spc_hpddm_levels_%" PetscInt_FMT "_eps_threshold so that at least one local deflation vector will be selected. If you don't want this to error out, compile --with-debugging=0", requested,
2698                data->N, reused, data->N, pcpre ? pcpre : "", pcpre ? pcpre : "", data->N);
2699   }
2700   /* these solvers are created after PCSetFromOptions() is called */
2701   if (pc->setfromoptionscalled) {
2702     for (n = 0; n < data->N; ++n) {
2703       if (data->levels[n]->ksp) PetscCall(KSPSetFromOptions(data->levels[n]->ksp));
2704       if (data->levels[n]->pc) PetscCall(PCSetFromOptions(data->levels[n]->pc));
2705     }
2706     pc->setfromoptionscalled = 0;
2707   }
2708   data->N += reused;
2709   if (data->share && swap) {
2710     /* swap back pointers so that variables follow the user-provided numbering */
2711     std::swap(C, data->aux);
2712     std::swap(uis, data->is);
2713     PetscCall(MatDestroy(&C));
2714     PetscCall(ISDestroy(&uis));
2715   }
2716   if (algebraic) PetscCall(MatDestroy(&data->aux));
2717   if (unsorted && unsorted != is[0]) {
2718     PetscCall(ISCopy(unsorted, data->is));
2719     PetscCall(ISDestroy(&unsorted));
2720   }
2721 #if PetscDefined(USE_DEBUG)
2722   PetscCheck((data->is && dis) || (!data->is && !dis), PETSC_COMM_SELF, PETSC_ERR_PLIB, "An IS pointer is NULL but not the other: input IS pointer (%p) v. output IS pointer (%p)", (void *)dis, (void *)data->is);
2723   if (data->is) {
2724     PetscCall(ISEqualUnsorted(data->is, dis, &flg));
2725     PetscCall(ISDestroy(&dis));
2726     PetscCheck(flg, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Input IS and output IS are not equal");
2727   }
2728   PetscCheck((data->aux && daux) || (!data->aux && !daux), PETSC_COMM_SELF, PETSC_ERR_PLIB, "A Mat pointer is NULL but not the other: input Mat pointer (%p) v. output Mat pointer (%p)", (void *)daux, (void *)data->aux);
2729   if (data->aux) {
2730     PetscCall(MatMultEqual(data->aux, daux, 20, &flg));
2731     PetscCall(MatDestroy(&daux));
2732     PetscCheck(flg, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Input Mat and output Mat are not equal");
2733   }
2734 #endif
2735   PetscFunctionReturn(PETSC_SUCCESS);
2736 }
2737 
2738 /*@
2739   PCHPDDMSetCoarseCorrectionType - Sets the coarse correction type.
2740 
2741   Collective
2742 
2743   Input Parameters:
2744 + pc   - preconditioner context
2745 - type - `PC_HPDDM_COARSE_CORRECTION_DEFLATED`, `PC_HPDDM_COARSE_CORRECTION_ADDITIVE`, `PC_HPDDM_COARSE_CORRECTION_BALANCED`, or `PC_HPDDM_COARSE_CORRECTION_NONE`
2746 
2747   Options Database Key:
2748 . -pc_hpddm_coarse_correction <deflated, additive, balanced, none> - type of coarse correction to apply
2749 
2750   Level: intermediate
2751 
2752 .seealso: [](ch_ksp), `PCHPDDMGetCoarseCorrectionType()`, `PCHPDDM`, `PCHPDDMCoarseCorrectionType`
2753 @*/
2754 PetscErrorCode PCHPDDMSetCoarseCorrectionType(PC pc, PCHPDDMCoarseCorrectionType type)
2755 {
2756   PetscFunctionBegin;
2757   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
2758   PetscValidLogicalCollectiveEnum(pc, type, 2);
2759   PetscTryMethod(pc, "PCHPDDMSetCoarseCorrectionType_C", (PC, PCHPDDMCoarseCorrectionType), (pc, type));
2760   PetscFunctionReturn(PETSC_SUCCESS);
2761 }
2762 
2763 /*@
2764   PCHPDDMGetCoarseCorrectionType - Gets the coarse correction type.
2765 
2766   Input Parameter:
2767 . pc - preconditioner context
2768 
2769   Output Parameter:
2770 . type - `PC_HPDDM_COARSE_CORRECTION_DEFLATED`, `PC_HPDDM_COARSE_CORRECTION_ADDITIVE`, `PC_HPDDM_COARSE_CORRECTION_BALANCED`, or `PC_HPDDM_COARSE_CORRECTION_NONE`
2771 
2772   Level: intermediate
2773 
2774 .seealso: [](ch_ksp), `PCHPDDMSetCoarseCorrectionType()`, `PCHPDDM`, `PCHPDDMCoarseCorrectionType`
2775 @*/
2776 PetscErrorCode PCHPDDMGetCoarseCorrectionType(PC pc, PCHPDDMCoarseCorrectionType *type)
2777 {
2778   PetscFunctionBegin;
2779   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
2780   if (type) {
2781     PetscAssertPointer(type, 2);
2782     PetscUseMethod(pc, "PCHPDDMGetCoarseCorrectionType_C", (PC, PCHPDDMCoarseCorrectionType *), (pc, type));
2783   }
2784   PetscFunctionReturn(PETSC_SUCCESS);
2785 }
2786 
2787 static PetscErrorCode PCHPDDMSetCoarseCorrectionType_HPDDM(PC pc, PCHPDDMCoarseCorrectionType type)
2788 {
2789   PC_HPDDM *data = (PC_HPDDM *)pc->data;
2790 
2791   PetscFunctionBegin;
2792   data->correction = type;
2793   PetscFunctionReturn(PETSC_SUCCESS);
2794 }
2795 
2796 static PetscErrorCode PCHPDDMGetCoarseCorrectionType_HPDDM(PC pc, PCHPDDMCoarseCorrectionType *type)
2797 {
2798   PC_HPDDM *data = (PC_HPDDM *)pc->data;
2799 
2800   PetscFunctionBegin;
2801   *type = data->correction;
2802   PetscFunctionReturn(PETSC_SUCCESS);
2803 }
2804 
2805 /*@
2806   PCHPDDMSetSTShareSubKSP - Sets whether the `KSP` in SLEPc `ST` and the fine-level subdomain solver should be shared.
2807 
2808   Input Parameters:
2809 + pc    - preconditioner context
2810 - share - whether the `KSP` should be shared or not
2811 
2812   Note:
2813   This is not the same as `PCSetReusePreconditioner()`. Given certain conditions (visible using -info), a symbolic factorization can be skipped
2814   when using a subdomain `PCType` such as `PCLU` or `PCCHOLESKY`.
2815 
2816   Level: advanced
2817 
2818 .seealso: [](ch_ksp), `PCHPDDM`, `PCHPDDMGetSTShareSubKSP()`
2819 @*/
2820 PetscErrorCode PCHPDDMSetSTShareSubKSP(PC pc, PetscBool share)
2821 {
2822   PetscFunctionBegin;
2823   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
2824   PetscTryMethod(pc, "PCHPDDMSetSTShareSubKSP_C", (PC, PetscBool), (pc, share));
2825   PetscFunctionReturn(PETSC_SUCCESS);
2826 }
2827 
2828 /*@
2829   PCHPDDMGetSTShareSubKSP - Gets whether the `KSP` in SLEPc `ST` and the fine-level subdomain solver is shared.
2830 
2831   Input Parameter:
2832 . pc - preconditioner context
2833 
2834   Output Parameter:
2835 . share - whether the `KSP` is shared or not
2836 
2837   Note:
2838   This is not the same as `PCGetReusePreconditioner()`. The return value is unlikely to be true, but when it is, a symbolic factorization can be skipped
2839   when using a subdomain `PCType` such as `PCLU` or `PCCHOLESKY`.
2840 
2841   Level: advanced
2842 
2843 .seealso: [](ch_ksp), `PCHPDDM`, `PCHPDDMSetSTShareSubKSP()`
2844 @*/
2845 PetscErrorCode PCHPDDMGetSTShareSubKSP(PC pc, PetscBool *share)
2846 {
2847   PetscFunctionBegin;
2848   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
2849   if (share) {
2850     PetscAssertPointer(share, 2);
2851     PetscUseMethod(pc, "PCHPDDMGetSTShareSubKSP_C", (PC, PetscBool *), (pc, share));
2852   }
2853   PetscFunctionReturn(PETSC_SUCCESS);
2854 }
2855 
2856 static PetscErrorCode PCHPDDMSetSTShareSubKSP_HPDDM(PC pc, PetscBool share)
2857 {
2858   PC_HPDDM *data = (PC_HPDDM *)pc->data;
2859 
2860   PetscFunctionBegin;
2861   data->share = share;
2862   PetscFunctionReturn(PETSC_SUCCESS);
2863 }
2864 
2865 static PetscErrorCode PCHPDDMGetSTShareSubKSP_HPDDM(PC pc, PetscBool *share)
2866 {
2867   PC_HPDDM *data = (PC_HPDDM *)pc->data;
2868 
2869   PetscFunctionBegin;
2870   *share = data->share;
2871   PetscFunctionReturn(PETSC_SUCCESS);
2872 }
2873 
2874 /*@
2875   PCHPDDMSetDeflationMat - Sets the deflation space used to assemble a coarser operator.
2876 
2877   Input Parameters:
2878 + pc - preconditioner context
2879 . is - index set of the local deflation matrix
2880 - U  - deflation sequential matrix stored as a `MATSEQDENSE`
2881 
2882   Level: advanced
2883 
2884 .seealso: [](ch_ksp), `PCHPDDM`, `PCDeflationSetSpace()`, `PCMGSetRestriction()`
2885 @*/
2886 PetscErrorCode PCHPDDMSetDeflationMat(PC pc, IS is, Mat U)
2887 {
2888   PetscFunctionBegin;
2889   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
2890   PetscValidHeaderSpecific(is, IS_CLASSID, 2);
2891   PetscValidHeaderSpecific(U, MAT_CLASSID, 3);
2892   PetscTryMethod(pc, "PCHPDDMSetDeflationMat_C", (PC, IS, Mat), (pc, is, U));
2893   PetscFunctionReturn(PETSC_SUCCESS);
2894 }
2895 
2896 static PetscErrorCode PCHPDDMSetDeflationMat_HPDDM(PC pc, IS is, Mat U)
2897 {
2898   PetscFunctionBegin;
2899   PetscCall(PCHPDDMSetAuxiliaryMat_Private(pc, is, U, PETSC_TRUE));
2900   PetscFunctionReturn(PETSC_SUCCESS);
2901 }
2902 
2903 PetscErrorCode HPDDMLoadDL_Private(PetscBool *found)
2904 {
2905   PetscBool flg;
2906   char      lib[PETSC_MAX_PATH_LEN], dlib[PETSC_MAX_PATH_LEN], dir[PETSC_MAX_PATH_LEN];
2907 
2908   PetscFunctionBegin;
2909   PetscAssertPointer(found, 1);
2910   PetscCall(PetscStrncpy(dir, "${PETSC_LIB_DIR}", sizeof(dir)));
2911   PetscCall(PetscOptionsGetString(nullptr, nullptr, "-hpddm_dir", dir, sizeof(dir), nullptr));
2912   PetscCall(PetscSNPrintf(lib, sizeof(lib), "%s/libhpddm_petsc", dir));
2913   PetscCall(PetscDLLibraryRetrieve(PETSC_COMM_SELF, lib, dlib, 1024, found));
2914 #if defined(SLEPC_LIB_DIR) /* this variable is passed during SLEPc ./configure when PETSc has not been configured   */
2915   if (!*found) {           /* with --download-hpddm since slepcconf.h is not yet built (and thus can't be included) */
2916     PetscCall(PetscStrncpy(dir, HPDDM_STR(SLEPC_LIB_DIR), sizeof(dir)));
2917     PetscCall(PetscSNPrintf(lib, sizeof(lib), "%s/libhpddm_petsc", dir));
2918     PetscCall(PetscDLLibraryRetrieve(PETSC_COMM_SELF, lib, dlib, 1024, found));
2919   }
2920 #endif
2921   if (!*found) { /* probable options for this to evaluate to PETSC_TRUE: system inconsistency (libhpddm_petsc moved by user?) or PETSc configured without --download-slepc */
2922     PetscCall(PetscOptionsGetenv(PETSC_COMM_SELF, "SLEPC_DIR", dir, sizeof(dir), &flg));
2923     if (flg) { /* if both PETSc and SLEPc are configured with --download-hpddm but PETSc has been configured without --download-slepc, one must ensure that libslepc is loaded before libhpddm_petsc */
2924       PetscCall(PetscSNPrintf(lib, sizeof(lib), "%s/lib/libslepc", dir));
2925       PetscCall(PetscDLLibraryRetrieve(PETSC_COMM_SELF, lib, dlib, 1024, found));
2926       PetscCheck(*found, PETSC_COMM_SELF, PETSC_ERR_PLIB, "%s not found but SLEPC_DIR=%s", lib, dir);
2927       PetscCall(PetscDLLibraryAppend(PETSC_COMM_SELF, &PetscDLLibrariesLoaded, dlib));
2928       PetscCall(PetscSNPrintf(lib, sizeof(lib), "%s/lib/libhpddm_petsc", dir)); /* libhpddm_petsc is always in the same directory as libslepc */
2929       PetscCall(PetscDLLibraryRetrieve(PETSC_COMM_SELF, lib, dlib, 1024, found));
2930     }
2931   }
2932   PetscCheck(*found, PETSC_COMM_SELF, PETSC_ERR_PLIB, "%s not found", lib);
2933   PetscCall(PetscDLLibraryAppend(PETSC_COMM_SELF, &PetscDLLibrariesLoaded, dlib));
2934   PetscFunctionReturn(PETSC_SUCCESS);
2935 }
2936 
2937 /*MC
2938    PCHPDDM - Interface with the HPDDM library.
2939 
2940    This `PC` may be used to build multilevel spectral domain decomposition methods based on the GenEO framework {cite}`spillane2011robust` {cite}`al2021multilevel`.
2941    It may be viewed as an alternative to spectral
2942    AMGe or `PCBDDC` with adaptive selection of constraints. The interface is explained in details in {cite}`jolivetromanzampini2020`
2943 
2944    The matrix used for building the preconditioner (Pmat) may be unassembled (`MATIS`), assembled (`MATAIJ`, `MATBAIJ`, or `MATSBAIJ`), hierarchical (`MATHTOOL`), `MATNORMAL`, `MATNORMALHERMITIAN`, or `MATSCHURCOMPLEMENT` (when `PCHPDDM` is used as part of an outer `PCFIELDSPLIT`).
2945 
2946    For multilevel preconditioning, when using an assembled or hierarchical Pmat, one must provide an auxiliary local `Mat` (unassembled local operator for GenEO) using
2947    `PCHPDDMSetAuxiliaryMat()`. Calling this routine is not needed when using a `MATIS` Pmat, assembly is done internally using `MatConvert()`.
2948 
2949    Options Database Keys:
2950 +   -pc_hpddm_define_subdomains <true, default=false>    - on the finest level, calls `PCASMSetLocalSubdomains()` with the `IS` supplied in `PCHPDDMSetAuxiliaryMat()`
2951                                                          (not relevant with an unassembled Pmat)
2952 .   -pc_hpddm_has_neumann <true, default=false>          - on the finest level, informs the `PC` that the local Neumann matrix is supplied in `PCHPDDMSetAuxiliaryMat()`
2953 -   -pc_hpddm_coarse_correction <type, default=deflated> - determines the `PCHPDDMCoarseCorrectionType` when calling `PCApply()`
2954 
2955    Options for subdomain solvers, subdomain eigensolvers (for computing deflation vectors), and the coarse solver can be set using the following options database prefixes.
2956 .vb
2957       -pc_hpddm_levels_%d_pc_
2958       -pc_hpddm_levels_%d_ksp_
2959       -pc_hpddm_levels_%d_eps_
2960       -pc_hpddm_levels_%d_p
2961       -pc_hpddm_levels_%d_mat_type
2962       -pc_hpddm_coarse_
2963       -pc_hpddm_coarse_p
2964       -pc_hpddm_coarse_mat_type
2965       -pc_hpddm_coarse_mat_filter
2966 .ve
2967 
2968    E.g., -pc_hpddm_levels_1_sub_pc_type lu -pc_hpddm_levels_1_eps_nev 10 -pc_hpddm_levels_2_p 4 -pc_hpddm_levels_2_sub_pc_type lu -pc_hpddm_levels_2_eps_nev 10
2969     -pc_hpddm_coarse_p 2 -pc_hpddm_coarse_mat_type baij will use 10 deflation vectors per subdomain on the fine "level 1",
2970     aggregate the fine subdomains into 4 "level 2" subdomains, then use 10 deflation vectors per subdomain on "level 2",
2971     and assemble the coarse matrix (of dimension 4 x 10 = 40) on two processes as a `MATBAIJ` (default is `MATSBAIJ`).
2972 
2973    In order to activate a "level N+1" coarse correction, it is mandatory to call -pc_hpddm_levels_N_eps_nev <nu> or -pc_hpddm_levels_N_eps_threshold <val>. The default -pc_hpddm_coarse_p value is 1, meaning that the coarse operator is aggregated on a single process.
2974 
2975    Level: intermediate
2976 
2977    Notes:
2978    This preconditioner requires that PETSc is built with SLEPc (``--download-slepc``).
2979 
2980    By default, the underlying concurrent eigenproblems
2981    are solved using SLEPc shift-and-invert spectral transformation. This is usually what gives the best performance for GenEO, cf.
2982    {cite}`spillane2011robust` {cite}`jolivet2013scalabledd`. As
2983    stated above, SLEPc options are available through -pc_hpddm_levels_%d_, e.g., -pc_hpddm_levels_1_eps_type arpack -pc_hpddm_levels_1_eps_nev 10
2984    -pc_hpddm_levels_1_st_type sinvert. There are furthermore three options related to the (subdomain-wise local) eigensolver that are not described in
2985    SLEPc documentation since they are specific to `PCHPDDM`.
2986 .vb
2987       -pc_hpddm_levels_1_st_share_sub_ksp
2988       -pc_hpddm_levels_%d_eps_threshold
2989       -pc_hpddm_levels_1_eps_use_inertia
2990 .ve
2991 
2992    The first option from the list only applies to the fine-level eigensolver, see `PCHPDDMSetSTShareSubKSP()`. The second option from the list is
2993    used to filter eigenmodes retrieved after convergence of `EPSSolve()` at "level N" such that eigenvectors used to define a "level N+1" coarse
2994    correction are associated to eigenvalues whose magnitude are lower or equal than -pc_hpddm_levels_N_eps_threshold. When using an `EPS` which cannot
2995    determine a priori the proper -pc_hpddm_levels_N_eps_nev such that all wanted eigenmodes are retrieved, it is possible to get an estimation of the
2996    correct value using the third option from the list, -pc_hpddm_levels_1_eps_use_inertia, see `MatGetInertia()`. In that case, there is no need
2997    to supply -pc_hpddm_levels_1_eps_nev. This last option also only applies to the fine-level (N = 1) eigensolver.
2998 
2999    See also {cite}`dolean2015introduction`, {cite}`al2022robust`, {cite}`al2022robustpd`, and {cite}`nataf2022recent`
3000 
3001 .seealso: [](ch_ksp), `PCCreate()`, `PCSetType()`, `PCType`, `PC`, `PCHPDDMSetAuxiliaryMat()`, `MATIS`, `PCBDDC`, `PCDEFLATION`, `PCTELESCOPE`, `PCASM`,
3002           `PCHPDDMSetCoarseCorrectionType()`, `PCHPDDMHasNeumannMat()`, `PCHPDDMSetRHSMat()`, `PCHPDDMSetDeflationMat()`, `PCHPDDMSetSTShareSubKSP()`,
3003           `PCHPDDMGetSTShareSubKSP()`, `PCHPDDMGetCoarseCorrectionType()`, `PCHPDDMGetComplexities()`
3004 M*/
3005 PETSC_EXTERN PetscErrorCode PCCreate_HPDDM(PC pc)
3006 {
3007   PC_HPDDM *data;
3008   PetscBool found;
3009 
3010   PetscFunctionBegin;
3011   if (!loadedSym) {
3012     PetscCall(HPDDMLoadDL_Private(&found));
3013     if (found) PetscCall(PetscDLLibrarySym(PETSC_COMM_SELF, &PetscDLLibrariesLoaded, nullptr, "PCHPDDM_Internal", (void **)&loadedSym));
3014   }
3015   PetscCheck(loadedSym, PETSC_COMM_SELF, PETSC_ERR_PLIB, "PCHPDDM_Internal symbol not found in loaded libhpddm_petsc");
3016   PetscCall(PetscNew(&data));
3017   pc->data                = data;
3018   data->Neumann           = PETSC_BOOL3_UNKNOWN;
3019   pc->ops->reset          = PCReset_HPDDM;
3020   pc->ops->destroy        = PCDestroy_HPDDM;
3021   pc->ops->setfromoptions = PCSetFromOptions_HPDDM;
3022   pc->ops->setup          = PCSetUp_HPDDM;
3023   pc->ops->apply          = PCApply_HPDDM;
3024   pc->ops->matapply       = PCMatApply_HPDDM;
3025   pc->ops->view           = PCView_HPDDM;
3026   pc->ops->presolve       = PCPreSolve_HPDDM;
3027 
3028   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCHPDDMSetAuxiliaryMat_C", PCHPDDMSetAuxiliaryMat_HPDDM));
3029   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCHPDDMHasNeumannMat_C", PCHPDDMHasNeumannMat_HPDDM));
3030   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCHPDDMSetRHSMat_C", PCHPDDMSetRHSMat_HPDDM));
3031   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCHPDDMSetCoarseCorrectionType_C", PCHPDDMSetCoarseCorrectionType_HPDDM));
3032   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCHPDDMGetCoarseCorrectionType_C", PCHPDDMGetCoarseCorrectionType_HPDDM));
3033   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCHPDDMSetSTShareSubKSP_C", PCHPDDMSetSTShareSubKSP_HPDDM));
3034   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCHPDDMGetSTShareSubKSP_C", PCHPDDMGetSTShareSubKSP_HPDDM));
3035   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCHPDDMSetDeflationMat_C", PCHPDDMSetDeflationMat_HPDDM));
3036   PetscFunctionReturn(PETSC_SUCCESS);
3037 }
3038 
3039 /*@C
3040   PCHPDDMInitializePackage - This function initializes everything in the `PCHPDDM` package. It is called from `PCInitializePackage()`.
3041 
3042   Level: developer
3043 
3044 .seealso: [](ch_ksp), `PetscInitialize()`
3045 @*/
3046 PetscErrorCode PCHPDDMInitializePackage(void)
3047 {
3048   char ename[32];
3049 
3050   PetscFunctionBegin;
3051   if (PCHPDDMPackageInitialized) PetscFunctionReturn(PETSC_SUCCESS);
3052   PCHPDDMPackageInitialized = PETSC_TRUE;
3053   PetscCall(PetscRegisterFinalize(PCHPDDMFinalizePackage));
3054   /* general events registered once during package initialization */
3055   /* some of these events are not triggered in libpetsc,          */
3056   /* but rather directly in libhpddm_petsc,                       */
3057   /* which is in charge of performing the following operations    */
3058 
3059   /* domain decomposition structure from Pmat sparsity pattern    */
3060   PetscCall(PetscLogEventRegister("PCHPDDMStrc", PC_CLASSID, &PC_HPDDM_Strc));
3061   /* Galerkin product, redistribution, and setup (not triggered in libpetsc)                */
3062   PetscCall(PetscLogEventRegister("PCHPDDMPtAP", PC_CLASSID, &PC_HPDDM_PtAP));
3063   /* Galerkin product with summation, redistribution, and setup (not triggered in libpetsc) */
3064   PetscCall(PetscLogEventRegister("PCHPDDMPtBP", PC_CLASSID, &PC_HPDDM_PtBP));
3065   /* next level construction using PtAP and PtBP (not triggered in libpetsc)                */
3066   PetscCall(PetscLogEventRegister("PCHPDDMNext", PC_CLASSID, &PC_HPDDM_Next));
3067   static_assert(PETSC_PCHPDDM_MAXLEVELS <= 9, "PETSC_PCHPDDM_MAXLEVELS value is too high");
3068   for (PetscInt i = 1; i < PETSC_PCHPDDM_MAXLEVELS; ++i) {
3069     PetscCall(PetscSNPrintf(ename, sizeof(ename), "PCHPDDMSetUp L%1" PetscInt_FMT, i));
3070     /* events during a PCSetUp() at level #i _except_ the assembly */
3071     /* of the Galerkin operator of the coarser level #(i + 1)      */
3072     PetscCall(PetscLogEventRegister(ename, PC_CLASSID, &PC_HPDDM_SetUp[i - 1]));
3073     PetscCall(PetscSNPrintf(ename, sizeof(ename), "PCHPDDMSolve L%1" PetscInt_FMT, i));
3074     /* events during a PCApply() at level #i _except_              */
3075     /* the KSPSolve() of the coarser level #(i + 1)                */
3076     PetscCall(PetscLogEventRegister(ename, PC_CLASSID, &PC_HPDDM_Solve[i - 1]));
3077   }
3078   PetscFunctionReturn(PETSC_SUCCESS);
3079 }
3080 
3081 /*@C
3082   PCHPDDMFinalizePackage - This function frees everything from the `PCHPDDM` package. It is called from `PetscFinalize()`.
3083 
3084   Level: developer
3085 
3086 .seealso: [](ch_ksp), `PetscFinalize()`
3087 @*/
3088 PetscErrorCode PCHPDDMFinalizePackage(void)
3089 {
3090   PetscFunctionBegin;
3091   PCHPDDMPackageInitialized = PETSC_FALSE;
3092   PetscFunctionReturn(PETSC_SUCCESS);
3093 }
3094 
3095 static PetscErrorCode MatMult_Harmonic(Mat A, Vec x, Vec y)
3096 {
3097   Harmonic h; /* [ A_00  A_01       ], furthermore, A_00 = [ A_loc,loc  A_loc,ovl ], thus, A_01 = [         ] */
3098               /* [ A_10  A_11  A_12 ]                      [ A_ovl,loc  A_ovl,ovl ]               [ A_ovl,1 ] */
3099   Vec sub;    /*  y = A x = R_loc R_0 [ A_00  A_01 ]^-1                                   R_loc = [  I_loc  ] */
3100               /*                      [ A_10  A_11 ]    R_1^T A_12 x                              [         ] */
3101   PetscFunctionBegin;
3102   PetscCall(MatShellGetContext(A, &h));
3103   PetscCall(VecSet(h->v, 0.0));
3104   PetscCall(VecGetSubVector(h->v, h->is[0], &sub));
3105   PetscCall(MatMult(h->A[0], x, sub));
3106   PetscCall(VecRestoreSubVector(h->v, h->is[0], &sub));
3107   PetscCall(KSPSolve(h->ksp, h->v, h->v));
3108   PetscCall(VecISCopy(h->v, h->is[1], SCATTER_REVERSE, y));
3109   PetscFunctionReturn(PETSC_SUCCESS);
3110 }
3111 
3112 static PetscErrorCode MatMultTranspose_Harmonic(Mat A, Vec y, Vec x)
3113 {
3114   Harmonic h;   /* x = A^T y =            [ A_00  A_01 ]^-T R_0^T R_loc^T y */
3115   Vec      sub; /*             A_12^T R_1 [ A_10  A_11 ]                    */
3116 
3117   PetscFunctionBegin;
3118   PetscCall(MatShellGetContext(A, &h));
3119   PetscCall(VecSet(h->v, 0.0));
3120   PetscCall(VecISCopy(h->v, h->is[1], SCATTER_FORWARD, y));
3121   PetscCall(KSPSolveTranspose(h->ksp, h->v, h->v));
3122   PetscCall(VecGetSubVector(h->v, h->is[0], &sub));
3123   PetscCall(MatMultTranspose(h->A[0], sub, x));
3124   PetscCall(VecRestoreSubVector(h->v, h->is[0], &sub));
3125   PetscFunctionReturn(PETSC_SUCCESS);
3126 }
3127 
3128 static PetscErrorCode MatProduct_AB_Harmonic(Mat S, Mat X, Mat Y, void *)
3129 {
3130   Harmonic h;
3131   Mat      A, B;
3132   Vec      a, b;
3133 
3134   PetscFunctionBegin;
3135   PetscCall(MatShellGetContext(S, &h));
3136   PetscCall(MatMatMult(h->A[0], X, MAT_INITIAL_MATRIX, PETSC_CURRENT, &A));
3137   PetscCall(MatCreateSeqDense(PETSC_COMM_SELF, h->ksp->pc->mat->rmap->n, A->cmap->n, nullptr, &B));
3138   for (PetscInt i = 0; i < A->cmap->n; ++i) {
3139     PetscCall(MatDenseGetColumnVecRead(A, i, &a));
3140     PetscCall(MatDenseGetColumnVecWrite(B, i, &b));
3141     PetscCall(VecISCopy(b, h->is[0], SCATTER_FORWARD, a));
3142     PetscCall(MatDenseRestoreColumnVecWrite(B, i, &b));
3143     PetscCall(MatDenseRestoreColumnVecRead(A, i, &a));
3144   }
3145   PetscCall(MatDestroy(&A));
3146   PetscCall(MatCreateSeqDense(PETSC_COMM_SELF, h->ksp->pc->mat->rmap->n, B->cmap->n, nullptr, &A));
3147   PetscCall(KSPMatSolve(h->ksp, B, A));
3148   PetscCall(MatDestroy(&B));
3149   for (PetscInt i = 0; i < A->cmap->n; ++i) {
3150     PetscCall(MatDenseGetColumnVecRead(A, i, &a));
3151     PetscCall(MatDenseGetColumnVecWrite(Y, i, &b));
3152     PetscCall(VecISCopy(a, h->is[1], SCATTER_REVERSE, b));
3153     PetscCall(MatDenseRestoreColumnVecWrite(Y, i, &b));
3154     PetscCall(MatDenseRestoreColumnVecRead(A, i, &a));
3155   }
3156   PetscCall(MatDestroy(&A));
3157   PetscFunctionReturn(PETSC_SUCCESS);
3158 }
3159 
3160 static PetscErrorCode MatDestroy_Harmonic(Mat A)
3161 {
3162   Harmonic h;
3163 
3164   PetscFunctionBegin;
3165   PetscCall(MatShellGetContext(A, &h));
3166   for (PetscInt i = 0; i < 5; ++i) PetscCall(ISDestroy(h->is + i));
3167   PetscCall(PetscFree(h->is));
3168   PetscCall(VecDestroy(&h->v));
3169   for (PetscInt i = 0; i < 2; ++i) PetscCall(MatDestroy(h->A + i));
3170   PetscCall(PetscFree(h->A));
3171   PetscCall(KSPDestroy(&h->ksp));
3172   PetscCall(PetscFree(h));
3173   PetscFunctionReturn(PETSC_SUCCESS);
3174 }
3175