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