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