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