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