Actual source code: hpddm.cxx
petsc-3.12.5 2020-03-29
1: #include <petsc/private/dmimpl.h>
2: #include <petsc/private/matimpl.h>
3: #include <petsc/private/petschpddm.h>
4: #include <petsc/private/pcimpl.h> /* this must be included after petschpddm.h so that _PCIMPL_H is not defined */
5: /* otherwise, it is assumed that one is compiling libhpddm_petsc => circular dependency */
7: static PetscErrorCode (*loadedSym)(HPDDM::Schwarz<PetscScalar>* const, IS const, IS, const Mat, Mat, std::vector<Vec>, PetscInt* const, PC_HPDDM_Level** const) = NULL;
9: static PetscBool citePC = PETSC_FALSE;
10: static const char hpddmCitationPC[] = "@inproceedings{jolivet2013scalable,\n\tTitle = {{Scalable Domain Decomposition Preconditioners For Heterogeneous Elliptic Problems}},\n\tAuthor = {Jolivet, Pierre and Hecht, Fr\'ed\'eric and Nataf, Fr\'ed\'eric and Prud'homme, Christophe},\n\tOrganization = {ACM},\n\tYear = {2013},\n\tSeries = {SC13},\n\tBooktitle = {Proceedings of the 2013 International Conference for High Performance Computing, Networking, Storage and Analysis}\n}\n";
12: static const char *PCHPDDMCoarseCorrectionTypes[] = { "deflated", "additive", "balanced" };
14: static PetscErrorCode PCReset_HPDDM(PC pc)
15: {
16: PC_HPDDM *data = (PC_HPDDM*)pc->data;
17: PetscInt i;
21: if (data->levels) {
22: for (i = 0; i < PETSC_HPDDM_MAXLEVELS && data->levels[i]; ++i) {
23: KSPDestroy(&data->levels[i]->ksp);
24: PCDestroy(&data->levels[i]->pc);
25: PetscFree(data->levels[i]);
26: }
27: PetscFree(data->levels);
28: }
30: ISDestroy(&data->is);
31: MatDestroy(&data->aux);
32: data->correction = PC_HPDDM_COARSE_CORRECTION_DEFLATED;
33: data->setup = NULL;
34: data->setup_ctx = NULL;
35: return(0);
36: }
38: static PetscErrorCode PCDestroy_HPDDM(PC pc)
39: {
40: PC_HPDDM *data = (PC_HPDDM*)pc->data;
44: PCReset_HPDDM(pc);
45: PetscFree(data);
46: PetscObjectChangeTypeName((PetscObject)pc, 0);
47: PetscObjectComposeFunction((PetscObject)pc, "PCHPDDMSetAuxiliaryMat_C", NULL);
48: PetscObjectComposeFunction((PetscObject)pc, "PCHPDDMSetCoarseCorrectionType_C", NULL);
49: PetscObjectComposeFunction((PetscObject)pc, "PCHPDDMGetCoarseCorrectionType_C", NULL);
50: return(0);
51: }
53: static PetscErrorCode PCHPDDMSetAuxiliaryMat_HPDDM(PC pc, IS is, Mat A, PetscErrorCode (*setup)(Mat, PetscReal, Vec, Vec, PetscReal, IS, void*), void* setup_ctx)
54: {
55: PC_HPDDM *data = (PC_HPDDM*)pc->data;
59: if (is) {
60: PetscObjectReference((PetscObject)is);
61: if (data->is) { /* new overlap definition resets the PC */
62: PCReset_HPDDM(pc);
63: pc->setfromoptionscalled = 0;
64: }
65: ISDestroy(&data->is);
66: data->is = is;
67: }
68: if (A) {
69: PetscObjectReference((PetscObject)A);
70: MatDestroy(&data->aux);
71: data->aux = A;
72: }
73: if (setup) {
74: data->setup = setup;
75: data->setup_ctx = setup_ctx;
76: }
77: return(0);
78: }
80: /*MC
81: PCHPDDMSetAuxiliaryMat - Sets the auxiliary matrix used by PCHPDDM for the concurrent GenEO eigenproblems at the finest level. As an example, in a finite element context with nonoverlapping subdomains plus (overlapping) ghost elements, this could be the unassembled (Neumann) local overlapping operator. As opposed to the assembled (Dirichlet) local overlapping operator obtained by summing neighborhood contributions at the interface of ghost elements.
83: Input Parameters:
84: + pc - preconditioner context
85: . is - index set of the local auxiliary, e.g., Neumann, matrix
86: . A - auxiliary sequential matrix
87: . setup - function for generating the auxiliary matrix
88: - setup_ctx - context for setup
90: Level: intermediate
92: .seealso: PCCreate(), PCSetType(), PCType (for list of available types), PC, MATIS
93: M*/
94: PetscErrorCode PCHPDDMSetAuxiliaryMat(PC pc, IS is, Mat A, PetscErrorCode (*setup)(Mat, PetscReal, Vec, Vec, PetscReal, IS, void*), void* setup_ctx)
95: {
102: PetscTryMethod(pc, "PCHPDDMSetAuxiliaryMat_C", (PC, IS, Mat, PetscErrorCode (*)(Mat, PetscReal, Vec, Vec, PetscReal, IS, void*), void*), (pc, is, A, setup, setup_ctx));
103: return(0);
104: }
106: static PetscErrorCode PCSetFromOptions_HPDDM(PetscOptionItems *PetscOptionsObject, PC pc)
107: {
108: PC_HPDDM *data = (PC_HPDDM*)pc->data;
109: PC_HPDDM_Level **levels = data->levels;
110: MPI_Comm comm;
111: char prefix[256];
112: int i = 1;
113: PetscMPIInt size, previous;
114: PetscInt n;
115: PetscBool flg = PETSC_TRUE;
119: if (!data->levels) {
120: PetscCalloc1(PETSC_HPDDM_MAXLEVELS, &levels);
121: data->levels = levels;
122: }
123: PetscOptionsHead(PetscOptionsObject, "PCHPDDM options");
124: PetscObjectGetComm((PetscObject)pc, &comm);
125: MPI_Comm_size(comm, &size);
126: previous = size;
127: while (i < PETSC_HPDDM_MAXLEVELS) {
128: PetscInt p = 1;
130: if (!data->levels[i - 1]) {
131: PetscNewLog(pc, data->levels + i - 1);
132: }
133: data->levels[i - 1]->parent = data;
134: /* if the previous level has a single process, it is not possible to coarsen further */
135: if (previous == 1 || !flg) break;
136: data->levels[i - 1]->nu = 0;
137: data->levels[i - 1]->threshold = -1.0;
138: PetscSNPrintf(prefix, sizeof(prefix), "-pc_hpddm_levels_%d_eps_nev", i);
139: PetscOptionsInt(prefix, "Local number of deflation vectors computed by SLEPc", "none", data->levels[i - 1]->nu, &data->levels[i - 1]->nu, NULL);
140: PetscSNPrintf(prefix, sizeof(prefix), "-pc_hpddm_levels_%d_eps_threshold", i);
141: PetscOptionsReal(prefix, "Local threshold for selecting deflation vectors returned by SLEPc", "none", data->levels[i - 1]->threshold, &data->levels[i - 1]->threshold, NULL);
142: /* if there is no prescribed coarsening, just break out of the loop */
143: if (data->levels[i - 1]->threshold <= 0.0 && data->levels[i - 1]->nu <= 0) break;
144: else {
145: ++i;
146: PetscSNPrintf(prefix, sizeof(prefix), "-pc_hpddm_levels_%d_p", i);
147: PetscOptionsRangeInt(prefix, "Number of processes used to assemble the next level (coarser) operator", "none", p, &p, &flg, 1, PetscMax(1, previous / 2));
148: if (flg) previous = p;
149: }
150: }
151: data->N = i;
152: n = 1;
153: PetscSNPrintf(prefix, sizeof(prefix), "-pc_hpddm_coarse_p");
154: PetscOptionsRangeInt(prefix, "Number of processes used to assemble the coarsest operator", "none", n, &n, NULL, 1, PetscMax(1, previous / 2));
155: PetscOptionsEList("-pc_hpddm_coarse_correction", "Type of coarse correction applied each iteration", "PCHPDDMSetCoarseCorrectionType", PCHPDDMCoarseCorrectionTypes, 3, PCHPDDMCoarseCorrectionTypes[PC_HPDDM_COARSE_CORRECTION_DEFLATED], &n, &flg);
156: if (flg) data->correction = PCHPDDMCoarseCorrectionType(n);
157: PetscOptionsTail();
158: while (i < PETSC_HPDDM_MAXLEVELS && data->levels[i]) {
159: PetscFree(data->levels[i++]);
160: }
161: return(0);
162: }
164: static PetscErrorCode PCApply_HPDDM(PC pc, Vec x, Vec y)
165: {
166: PC_HPDDM *data = (PC_HPDDM*)pc->data;
170: PetscCitationsRegister(hpddmCitationPC, &citePC);
171: if (data->levels[0]->ksp) {
172: KSPSolve(data->levels[0]->ksp, x, y);
173: } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "No KSP attached to PCHPDDM");
174: return(0);
175: }
177: /*
178: PCHPDDMGetComplexities - Computes the grid and operator complexities.
180: Input Parameter:
181: . pc - preconditioner context
183: Output Parameters:
184: + gc - grid complexity = sum_i(m_i) / m_1
185: . oc - operator complexity = sum_i(nnz_i) / nnz_1
187: Notes:
188: PCGAMG does not follow the usual convention and names the grid complexity what is usually referred to as the operator complexity. PCHPDDM follows what is found in the literature, and in particular, what you get with PCHYPRE and -pc_hypre_boomeramg_print_statistics.
190: Level: advanced
192: .seealso: PCMGGetGridComplexity(), PCHPDDM
193: */
194: static PetscErrorCode PCHPDDMGetComplexities(PC pc, PetscReal *gc, PetscReal *oc)
195: {
196: PC_HPDDM *data = (PC_HPDDM*)pc->data;
197: MatInfo info;
198: PetscInt n, m, nnz1 = 1, m1 = 1;
202: for (n = 0, *gc = 0, *oc = 0; n < data->N; ++n) {
203: if (data->levels[n]->ksp) {
204: Mat P;
205: KSPGetOperators(data->levels[n]->ksp, NULL, &P);
206: MatGetSize(P, &m, NULL);
207: *gc += m;
208: MatGetInfo(P, MAT_GLOBAL_SUM, &info);
209: *oc += (PetscReal)info.nz_used;
210: if (n == 0) {
211: m1 = m;
212: nnz1 = info.nz_used;
213: }
214: }
215: }
216: *gc /= (PetscReal)m1;
217: *oc /= (PetscReal)nnz1;
218: return(0);
219: }
221: static PetscErrorCode PCView_HPDDM(PC pc, PetscViewer viewer)
222: {
223: PC_HPDDM *data = (PC_HPDDM*)pc->data;
224: PetscViewer subviewer;
225: PetscSubcomm subcomm;
226: PetscReal oc, gc;
227: PetscInt i, tabs;
228: PetscMPIInt size, color, rank;
229: PetscBool ascii;
233: PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &ascii);
234: if (ascii) {
235: PetscViewerASCIIPrintf(viewer, "level%s: %D\n", data->N > 1 ? "s" : "", data->N);
236: PCHPDDMGetComplexities(pc, &gc, &oc);
237: if (data->N > 1) {
238: PetscViewerASCIIPrintf(viewer, "coarse correction: %s\n", PCHPDDMCoarseCorrectionTypes[data->correction]);
239: PetscViewerASCIIPrintf(viewer, "on process #0, value%s (+ threshold%s if available) for selecting deflation vectors:", data->N > 2 ? "s" : "", data->N > 2 ? "s" : "");
240: PetscViewerASCIIGetTab(viewer, &tabs);
241: PetscViewerASCIISetTab(viewer, 0);
242: for (i = 1; i < data->N; ++i) {
243: PetscViewerASCIIPrintf(viewer, " %D", data->levels[i - 1]->nu);
244: if (data->levels[i - 1]->threshold > -0.1) {
245: PetscViewerASCIIPrintf(viewer, " (%g)", (double)data->levels[i - 1]->threshold);
246: }
247: }
248: PetscViewerASCIIPrintf(viewer, "\n");
249: PetscViewerASCIISetTab(viewer, tabs);
250: }
251: PetscViewerASCIIPrintf(viewer, "grid and operator complexities: %g %g\n", (double)gc, (double)oc);
252: if (data->levels[0]->ksp) {
253: KSPView(data->levels[0]->ksp, viewer);
254: if (data->levels[0]->pc) {
255: PCView(data->levels[0]->pc, viewer);
256: }
257: for (i = 1; i < data->N; ++i) {
258: if (data->levels[i]->ksp) color = 1;
259: else color = 0;
260: MPI_Comm_size(PetscObjectComm((PetscObject)pc), &size);
261: MPI_Comm_size(PetscObjectComm((PetscObject)pc), &rank);
262: PetscSubcommCreate(PetscObjectComm((PetscObject)pc), &subcomm);
263: PetscSubcommSetNumber(subcomm, PetscMin(size, 2));
264: PetscSubcommSetTypeGeneral(subcomm, color, rank);
265: PetscViewerASCIIPushTab(viewer);
266: PetscViewerGetSubViewer(viewer, PetscSubcommChild(subcomm), &subviewer);
267: if (color == 1) {
268: KSPView(data->levels[i]->ksp, subviewer);
269: if (data->levels[i]->pc) {
270: PCView(data->levels[i]->pc, subviewer);
271: }
272: PetscViewerFlush(subviewer);
273: }
274: PetscViewerRestoreSubViewer(viewer, PetscSubcommChild(subcomm), &subviewer);
275: PetscViewerASCIIPopTab(viewer);
276: PetscSubcommDestroy(&subcomm);
277: PetscViewerFlush(viewer);
278: }
279: }
280: }
281: return(0);
282: }
284: static PetscErrorCode PCHPDDMShellSetUp(PC pc)
285: {
286: PC_HPDDM_Level *ctx;
287: Mat A, P;
288: Vec x;
289: const char *pcpre;
293: PCShellGetContext(pc, (void**)&ctx);
294: KSPGetOptionsPrefix(ctx->ksp, &pcpre);
295: KSPGetOperators(ctx->ksp, &A, &P);
296: /* smoother */
297: PCSetOptionsPrefix(ctx->pc, pcpre);
298: PCSetOperators(ctx->pc, A, P);
299: if (!ctx->v[0]) {
300: VecDuplicateVecs(ctx->D, 1, &ctx->v[0]);
301: if (!std::is_same<PetscScalar, PetscReal>::value) {
302: VecDestroy(&ctx->D);
303: }
304: MatCreateVecs(A, &x, NULL);
305: VecDuplicateVecs(x, 2, &ctx->v[1]);
306: VecDestroy(&x);
307: }
308: return(0);
309: }
311: PETSC_STATIC_INLINE PetscErrorCode PCHPDDMDeflate_Private(PC pc, Vec x, Vec y)
312: {
313: PC_HPDDM_Level *ctx;
314: PetscScalar *out;
318: PCShellGetContext(pc, (void**)&ctx);
319: /* going from PETSc to HPDDM numbering */
320: VecScatterBegin(ctx->scatter, x, ctx->v[0][0], INSERT_VALUES, SCATTER_FORWARD);
321: VecScatterEnd(ctx->scatter, x, ctx->v[0][0], INSERT_VALUES, SCATTER_FORWARD);
322: VecGetArray(ctx->v[0][0], &out);
323: ctx->P->deflation<false>(NULL, out, 1); /* y = Q x */
324: VecRestoreArray(ctx->v[0][0], &out);
325: /* going from HPDDM to PETSc numbering */
326: VecScatterBegin(ctx->scatter, ctx->v[0][0], y, INSERT_VALUES, SCATTER_REVERSE);
327: VecScatterEnd(ctx->scatter, ctx->v[0][0], y, INSERT_VALUES, SCATTER_REVERSE);
328: return(0);
329: }
331: /*MC
332: PCHPDDMShellApply - Applies a (2) deflated, (1) additive, or (3) balanced coarse correction. In what follows, E = Z Pmat Z^T and Q = Z^T E^-1 Z.
334: .vb
335: (1) y = Pmat^-1 x + Q x,
336: (2) y = Pmat^-1 (I - Amat Q) x + Q x (default),
337: (3) y = (I - Q Amat^T) Pmat^-1 (I - Amat Q) x + Q x.
338: .ve
340: Input Parameters:
341: + pc - preconditioner context
342: . x - input vector
343: - y - output vector
345: Application Interface Routine: PCApply()
347: Notes:
348: 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.
349: 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.
350: (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.
352: Level: advanced
354: .seealso: PCHPDDM, PCHPDDMCoarseCorrectionType
355: M*/
356: static PetscErrorCode PCHPDDMShellApply(PC pc, Vec x, Vec y)
357: {
358: PC_HPDDM_Level *ctx;
359: Mat A;
363: PCShellGetContext(pc, (void**)&ctx);
364: if (ctx->P) {
365: KSPGetOperators(ctx->ksp, &A, NULL);
366: PCHPDDMDeflate_Private(pc, x, y); /* y = Q x */
367: if (ctx->parent->correction == PC_HPDDM_COARSE_CORRECTION_DEFLATED || ctx->parent->correction == PC_HPDDM_COARSE_CORRECTION_BALANCED) {
368: MatMult(A, y, ctx->v[1][0]); /* y = A Q x */
369: VecWAXPY(ctx->v[1][1], -1.0, ctx->v[1][0], x); /* y = (I - A Q) x */
370: PCApply(ctx->pc, ctx->v[1][1], ctx->v[1][0]); /* y = M^-1 (I - A Q) x */
371: if (ctx->parent->correction == PC_HPDDM_COARSE_CORRECTION_BALANCED) {
372: MatMultTranspose(A, ctx->v[1][0], ctx->v[1][1]); /* z = A^T M^-1 (I - A Q) x */
373: PCHPDDMDeflate_Private(pc, ctx->v[1][1], ctx->v[1][1]);
374: VecAXPY(ctx->v[1][0], -1.0, ctx->v[1][1]); /* y = (I - Q A^T) M^-1 (I - A Q) x */
375: }
376: VecAXPY(y, 1.0, ctx->v[1][0]); /* y = y + Q x */
377: } else if (ctx->parent->correction == PC_HPDDM_COARSE_CORRECTION_ADDITIVE) {
378: PCApply(ctx->pc, x, ctx->v[1][0]);
379: VecAXPY(y, 1.0, ctx->v[1][0]); /* y = M^-1 x + Q x */
380: } else SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_PLIB, "PCSHELL from PCHPDDM called with an unknown PCHPDDMCoarseCorrectionType %d", ctx->parent->correction);
381: } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "PCSHELL from PCHPDDM called with no HPDDM object");
382: return(0);
383: }
385: static PetscErrorCode PCHPDDMShellDestroy(PC pc)
386: {
387: PC_HPDDM_Level *ctx;
391: PCShellGetContext(pc, (void**)&ctx);
392: HPDDM::Schwarz<PetscScalar>::destroy(ctx, PETSC_TRUE);
393: VecDestroyVecs(1, &ctx->v[0]);
394: VecDestroyVecs(2, &ctx->v[1]);
395: VecDestroy(&ctx->D);
396: VecScatterDestroy(&ctx->scatter);
397: PCDestroy(&ctx->pc);
398: return(0);
399: }
401: static PetscErrorCode PCHPDDMSetUpNeumannOverlap_Private(PC pc)
402: {
403: PC_HPDDM *data = (PC_HPDDM*)pc->data;
407: if (data->setup) {
408: Mat P;
409: Vec x, xt = NULL;
410: PetscReal t = 0.0, s = 0.0;
412: PCGetOperators(pc, NULL, &P);
413: PetscObjectQuery((PetscObject)P, "__SNES_latest_X", (PetscObject*)&x);
414: PetscStackPush("PCHPDDM Neumann callback");
415: (*data->setup)(data->aux, t, x, xt, s, data->is, data->setup_ctx);
416: PetscStackPop;
417: }
418: return(0);
419: }
421: static PetscErrorCode PCSetUp_HPDDM(PC pc)
422: {
423: PC_HPDDM *data = (PC_HPDDM*)pc->data;
424: PC inner;
425: Mat *sub, A, P, N, C, uaux = NULL;
426: Vec xin, v;
427: std::vector<Vec> initial;
428: IS is[1], loc, uis = data->is;
429: ISLocalToGlobalMapping l2g;
430: MPI_Comm comm;
431: char prefix[256];
432: const char *pcpre;
433: const PetscScalar* const *ev;
434: PetscInt n, requested = data->N;
435: PetscBool subdomains = PETSC_FALSE, outer = PETSC_FALSE, ismatis;
436: DM dm;
437: PetscErrorCode ierr;
440: if (!data->levels[0]) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Not a single level allocated");
441: PCGetOptionsPrefix(pc, &pcpre);
442: PCGetOperators(pc, &A, &P);
443: PetscObjectGetComm((PetscObject)pc, &comm);
444: if (!data->levels[0]->ksp) {
445: KSPCreate(comm, &data->levels[0]->ksp);
446: PetscSNPrintf(prefix, sizeof(prefix), "%spc_hpddm_%s_", pcpre ? pcpre : "", data->N > 1 ? "levels_1" : "coarse");
447: KSPSetOptionsPrefix(data->levels[0]->ksp, prefix);
448: KSPSetType(data->levels[0]->ksp, KSPPREONLY);
449: } else {
450: const int *addr = data->levels[0]->P ? data->levels[0]->P->getAddrLocal() : &HPDDM::i__0;
452: if (addr != &HPDDM::i__0) {
453: /* reuse previously computed eigenvectors */
454: ev = data->levels[0]->P->getVectors();
455: if (ev) {
456: initial.reserve(*addr);
457: VecCreateSeqWithArray(PETSC_COMM_SELF, 1, data->levels[0]->P->getDof(), ev[0], &xin);
458: for (n = 0; n < *addr; ++n) {
459: VecDuplicate(xin, &v);
460: VecPlaceArray(xin, ev[n]);
461: VecCopy(xin, v);
462: initial.emplace_back(v);
463: VecResetArray(xin);
464: }
465: VecDestroy(&xin);
466: }
467: }
468: /* reset coarser levels */
469: for (n = 1; n < PETSC_HPDDM_MAXLEVELS && data->levels[n]; ++n) {
470: KSPDestroy(&data->levels[n]->ksp);
471: PCDestroy(&data->levels[n]->pc);
472: }
473: }
474: KSPSetOperators(data->levels[0]->ksp, A, P);
476: PetscObjectTypeCompare((PetscObject)P, MATIS, &ismatis);
477: if (!data->is && !ismatis) {
478: PetscErrorCode (*create)(DM, IS*, Mat*, PetscErrorCode (**)(Mat, PetscReal, Vec, Vec, PetscReal, IS, void*), void**) = NULL;
479: PetscErrorCode (*usetup)(Mat, PetscReal, Vec, Vec, PetscReal, IS, void*) = NULL;
480: void *uctx = NULL;
482: /* first see if we can get the data from the DM */
483: MatGetDM(P, &dm);
484: if (!dm) {
485: MatGetDM(A, &dm);
486: }
487: if (!dm) {
488: PCGetDM(pc, &dm);
489: }
490: if (dm) { /* this is the hook for DMPLEX and DMDA */
491: PetscObjectQueryFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", &create);
492: if (create) {
493: (*create)(dm, &uis, &uaux, &usetup, &uctx);
494: }
495: }
496: if (!create) {
497: if (!uis) {
498: PetscObjectQuery((PetscObject)pc, "_PCHPDDM_Neumann_IS", (PetscObject*)&uis);
499: PetscObjectReference((PetscObject)uis);
500: }
501: if (!uaux) {
502: PetscObjectQuery((PetscObject)pc, "_PCHPDDM_Neumann_Mat", (PetscObject*)&uaux);
503: PetscObjectReference((PetscObject)uaux);
504: }
505: }
506: PCHPDDMSetAuxiliaryMat(pc, uis, uaux, usetup, uctx);
507: MatDestroy(&uaux);
508: ISDestroy(&uis);
509: }
511: if (!ismatis) {
512: PCHPDDMSetUpNeumannOverlap_Private(pc);
513: }
515: if (data->is || (ismatis && data->N > 1)) {
516: if (ismatis) {
517: std::initializer_list<std::string> list = { MATSEQBAIJ, MATSEQSBAIJ };
518: MatISGetLocalMat(P, &N);
519: std::initializer_list<std::string>::const_iterator it = std::find(list.begin(), list.end(), ((PetscObject)N)->type_name);
520: MatISRestoreLocalMat(P, &N);
521: switch (std::distance(list.begin(), it)) {
522: case 0:
523: MatConvert(P, MATMPIBAIJ, MAT_INITIAL_MATRIX, &C);
524: break;
525: case 1:
526: /* MatCreateSubMatrices does not work with [Seq|MPI]SBAIJ and unsorted ISes, so convert to MPIBAIJ */
527: MatConvert(P, MATMPIBAIJ, MAT_INITIAL_MATRIX, &C);
528: MatSetOption(C, MAT_SYMMETRIC, PETSC_TRUE);
529: break;
530: default:
531: MatConvert(P, MATMPIAIJ, MAT_INITIAL_MATRIX, &C);
532: }
533: MatGetLocalToGlobalMapping(P, &l2g, NULL);
534: PetscObjectReference((PetscObject)P);
535: KSPSetOperators(data->levels[0]->ksp, A, C);
536: std::swap(C, P);
537: ISLocalToGlobalMappingGetSize(l2g, &n);
538: ISCreateStride(PETSC_COMM_SELF, n, 0, 1, &loc);
539: ISLocalToGlobalMappingApplyIS(l2g, loc, &is[0]);
540: ISDestroy(&loc);
541: } else {
542: is[0] = data->is;
543: PetscOptionsGetBool(NULL, pcpre, "-pc_hpddm_define_subdomains", &subdomains, NULL);
544: ISCreateStride(PetscObjectComm((PetscObject)data->is), P->rmap->n, P->rmap->rstart, 1, &loc);
545: }
546: if (data->N > 1 && (data->aux || ismatis)) {
547: if (!loadedSym) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "HPDDM library not loaded, cannot use more than one level");
548: MatSetOption(P, MAT_SUBMAT_SINGLEIS, PETSC_TRUE);
549: if (ismatis) {
550: /* needed by HPDDM (currently) so that the partition of unity is 0 on subdomain interfaces */
551: MatIncreaseOverlap(P, 1, is, 1);
552: ISDestroy(&data->is);
553: data->is = is[0];
554: #if defined(PETSC_USE_DEBUG)
555: } else {
556: PetscBool equal;
557: IS intersect;
559: ISIntersect(data->is, loc, &intersect);
560: ISEqualUnsorted(loc, intersect, &equal);
561: ISDestroy(&intersect);
562: if (!equal) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "IS of the auxiliary Mat does not include all local rows of A");
563: #endif
564: }
565: MatCreateSubMatrices(P, 1, is, is, MAT_INITIAL_MATRIX, &sub);
566: /* Vec holding the partition of unity */
567: if (!data->levels[0]->D) {
568: ISGetLocalSize(data->is, &n);
569: VecCreateMPI(PETSC_COMM_SELF, n, PETSC_DETERMINE, &data->levels[0]->D);
570: }
571: if (!data->levels[0]->scatter) {
572: MatCreateVecs(P, &xin, NULL);
573: if (ismatis) {
574: MatDestroy(&P);
575: }
576: VecScatterCreate(xin, data->is, data->levels[0]->D, NULL, &data->levels[0]->scatter);
577: VecDestroy(&xin);
578: }
579: if (data->levels[0]->P) {
580: /* if the pattern is the same and PCSetUp has previously succeeded, reuse HPDDM buffers and connectivity */
581: HPDDM::Schwarz<PetscScalar>::destroy(data->levels[0], pc->setupcalled < 1 || pc->flag == DIFFERENT_NONZERO_PATTERN ? PETSC_TRUE : PETSC_FALSE);
582: }
583: if (!data->levels[0]->P) data->levels[0]->P = new HPDDM::Schwarz<PetscScalar>();
584: (*loadedSym)(data->levels[0]->P, loc, data->is, sub[0], ismatis ? C : data->aux, initial, &data->N, data->levels);
585: MatDestroySubMatrices(1, &sub);
586: if (ismatis) data->is = NULL;
587: for (n = 0; n < data->N - 1; ++n) {
588: if (data->levels[n]->P) {
589: PC spc;
591: /* force the PC to be PCSHELL to do the coarse grid corrections */
592: KSPSetSkipPCSetFromOptions(data->levels[n]->ksp, PETSC_TRUE);
593: KSPGetPC(data->levels[n]->ksp, &spc);
594: PCSetType(spc, PCSHELL);
595: PCShellSetContext(spc, data->levels[n]);
596: PCShellSetSetUp(spc, PCHPDDMShellSetUp);
597: PCShellSetApply(spc, PCHPDDMShellApply);
598: PCShellSetDestroy(spc, PCHPDDMShellDestroy);
599: if (!data->levels[n]->pc) {
600: PCCreate(PetscObjectComm((PetscObject)data->levels[n]->ksp), &data->levels[n]->pc);
601: }
602: PCSetUp(spc);
603: }
604: }
605: } else outer = PETSC_TRUE;
606: if (!ismatis && subdomains) {
607: if (outer) {
608: KSPGetPC(data->levels[0]->ksp, &inner);
609: PCASMSetLocalSubdomains(inner, 1, is, &loc);
610: } else {
611: PCASMSetLocalSubdomains(data->levels[0]->pc, 1, is, &loc);
612: }
613: }
614: ISDestroy(&loc);
615: } else data->N = 1; /* enforce this value to 1 if there is no way to build another level */
616: if (requested != data->N) {
617: PetscInfo4(pc, "%D levels requested, only %D built. Options for level(s) > %D, including -%spc_hpddm_coarse_ will not be taken into account.\n", requested, data->N, data->N, pcpre ? pcpre : "");
618: PetscInfo2(pc, "It is best to tune parameters, e.g., a higher value for -%spc_hpddm_levels_%D_eps_threshold so that at least one local deflation vector will be selected.\n", pcpre ? pcpre : "", data->N);
619: /* cannot use PCHPDDMShellDestroy because PCSHELL not set for unassembled levels */
620: for (n = data->N - 1; n < requested - 1; ++n) {
621: if (data->levels[n]->P) {
622: HPDDM::Schwarz<PetscScalar>::destroy(data->levels[n], PETSC_TRUE);
623: VecDestroyVecs(1, &data->levels[n]->v[0]);
624: VecDestroyVecs(2, &data->levels[n]->v[1]);
625: VecDestroy(&data->levels[n]->D);
626: VecScatterDestroy(&data->levels[n]->scatter);
627: }
628: }
629: #if defined(PETSC_USE_DEBUG)
630: SETERRQ6(PetscObjectComm((PetscObject)pc), PETSC_ERR_ARG_WRONG, "%D levels requested, only %D built. Options for level(s) > %D, 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_%D_eps_threshold so that at least one local deflation vector will be selected. If you don't want this to error out, compile --with-debugging=0", requested, data->N, data->N, pcpre ? pcpre : "", pcpre ? pcpre : "", data->N);
631: #endif
632: }
634: /* these solvers are created after PCSetFromOptions is called */
635: if (pc->setfromoptionscalled) {
636: for (n = 0; n < data->N; ++n) {
637: if (data->levels[n]->ksp) {
638: KSPSetFromOptions(data->levels[n]->ksp);
639: }
640: if (data->levels[n]->pc) {
641: PCSetFromOptions(data->levels[n]->pc);
642: }
643: }
644: pc->setfromoptionscalled = 0;
645: }
646: return(0);
647: }
649: /*@C
650: PCHPDDMSetCoarseCorrectionType - Sets the coarse correction type.
652: Input Parameters:
653: + pc - preconditioner context
654: - type - PC_HPDDM_COARSE_CORRECTION_DEFLATED, PC_HPDDM_COARSE_CORRECTION_ADDITIVE, or PC_HPDDM_COARSE_CORRECTION_BALANCED
656: Options Database Key:
657: . -pc_hpddm_coarse_correction <deflated, additive, balanced> - type of coarse correction to apply
659: Level: intermediate
661: .seealso: PCHPDDMGetCoarseCorrectionType(), PCHPDDM, PCHPDDMCoarseCorrectionType
662: @*/
663: PetscErrorCode PCHPDDMSetCoarseCorrectionType(PC pc, PCHPDDMCoarseCorrectionType type)
664: {
669: PetscTryMethod(pc, "PCHPDDMSetCoarseCorrectionType_C", (PC, PCHPDDMCoarseCorrectionType), (pc, type));
670: return(0);
671: }
673: /*@C
674: PCHPDDMGetCoarseCorrectionType - Gets the coarse correction type.
676: Input Parameter:
677: . pc - preconditioner context
679: Output Parameter:
680: - type - PC_HPDDM_COARSE_CORRECTION_DEFLATED, PC_HPDDM_COARSE_CORRECTION_ADDITIVE, or PC_HPDDM_COARSE_CORRECTION_BALANCED
682: Level: intermediate
684: .seealso: PCHPDDMSetCoarseCorrectionType(), PCHPDDM, PCHPDDMCoarseCorrectionType
685: @*/
686: PetscErrorCode PCHPDDMGetCoarseCorrectionType(PC pc, PCHPDDMCoarseCorrectionType *type)
687: {
692: PetscUseMethod(pc, "PCHPDDMGetCoarseCorrectionType_C", (PC, PCHPDDMCoarseCorrectionType*), (pc, type));
693: return(0);
694: }
696: static PetscErrorCode PCHPDDMSetCoarseCorrectionType_HPDDM(PC pc, PCHPDDMCoarseCorrectionType type)
697: {
698: PC_HPDDM *data = (PC_HPDDM*)pc->data;
701: if (type < 0 || type > 2) SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_ARG_UNKNOWN_TYPE, "Unknown PCHPDDMCoarseCorrectionType %d", type);
702: data->correction = type;
703: return(0);
704: }
706: static PetscErrorCode PCHPDDMGetCoarseCorrectionType_HPDDM(PC pc, PCHPDDMCoarseCorrectionType *type)
707: {
708: PC_HPDDM *data = (PC_HPDDM*)pc->data;
711: *type = data->correction;
712: return(0);
713: }
715: /*MC
716: PCHPDDM - Interface with the HPDDM library.
718: This PC may be used to build multilevel spectral domain decomposition methods based on the GenEO framework [2011, 2019]. It may be viewed as an alternative to spectral AMGe or PCBDDC with adaptive selection of constraints. A chronological bibliography of relevant publications linked with PC available in HPDDM through PCHPDDM may be found below.
720: The matrix to be preconditioned (Pmat) may be unassembled (MATIS) or assembled (MATMPIAIJ, MATMPIBAIJ, or MATMPISBAIJ). For multilevel preconditioning, when using an assembled Pmat, one must provide an auxiliary local Mat (unassembled local operator for GenEO) using PCHPDDMSetAuxiliaryMat. Calling this routine is not needed when using a MATIS Pmat (assembly done internally using MatConvert).
722: Options Database Keys:
723: + -pc_hpddm_define_subdomains <true, false> - on the finest level, calls PCASMSetLocalSubdomains with the IS supplied in PCHPDDMSetAuxiliaryMat (only relevant with an assembled Pmat)
724: - -pc_hpddm_coarse_correction <type, default=deflated> - determines the PCHPDDMCoarseCorrectionType when calling PCApply
726: Options for subdomain solvers, subdomain eigensolvers (for computing deflation vectors), and the coarse solver can be set with
727: .vb
728: -pc_hpddm_levels_%d_pc_
729: -pc_hpddm_levels_%d_ksp_
730: -pc_hpddm_levels_%d_eps_
731: -pc_hpddm_levels_%d_p
732: -pc_hpddm_levels_%d_mat_type_
733: -pc_hpddm_coarse_
734: -pc_hpddm_coarse_p
735: -pc_hpddm_coarse_mat_type_
736: .ve
737: 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 -pc_hpddm_coarse_p 2 -pc_hpddm_coarse_mat_type baij will use 10 deflation vectors per subdomain on the fine "level 1", aggregate the fine subdomains into 4 "level 2" subdomains, then use 10 deflation vectors per subdomain on "level 2", and assemble the coarse matrix (of dimension 4 x 10 = 40) on two processes as a MATMPIBAIJ (default is MATMPISBAIJ).
739: In order to activate a "level N+1" coarse correction, it is mandatory to call -pc_hpddm_levels_N_eps_nev <nu> or -pc_hpddm_levels_N_eps_threshold <val>. The default -pc_hpddm_coarse_p value is 1, meaning that the coarse operator is aggregated on a single process.
741: This preconditioner requires that you build PETSc with SLEPc (--download-slepc=1). By default, the underlying concurrent eigenproblems are solved using SLEPc shift-and-invert spectral transformation. This is usually what gives the best performance for GenEO, cf. [2011, 2013]. As 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_threshold 0.1 -pc_hpddm_levels_1_st_type sinvert.
743: References:
744: + 2011 - A robust two-level domain decomposition preconditioner for systems of PDEs. Spillane, Dolean, Hauret, Nataf, Pechstein, and Scheichl. Comptes Rendus Mathematique.
745: . 2013 - Scalable Domain Decomposition Preconditioners For Heterogeneous Elliptic Problems. Jolivet, Hecht, Nataf, and Prud'homme. SC13.
746: . 2015 - An Introduction to Domain Decomposition Methods: Algorithms, Theory, and Parallel Implementation. Dolean, Jolivet, and Nataf. SIAM.
747: - 2019 - A Multilevel Schwarz Preconditioner Based on a Hierarchy of Robust Coarse Spaces. Al Daas, Grigori, Jolivet, and Tournier.
749: Level: intermediate
751: .seealso: PCCreate(), PCSetType(), PCType (for list of available types), PC, PCHPDDMSetAuxiliaryMat(), MATIS, PCBDDC, PCDEFLATION, PCTELESCOPE
752: M*/
753: PETSC_EXTERN PetscErrorCode PCCreate_HPDDM(PC pc)
754: {
755: PetscBool found;
756: char lib[PETSC_MAX_PATH_LEN], dlib[PETSC_MAX_PATH_LEN], dir[PETSC_MAX_PATH_LEN];
757: PC_HPDDM *data;
761: if (!loadedSym) {
762: PetscStrcpy(dir, "${PETSC_LIB_DIR}");
763: PetscOptionsGetString(NULL, NULL, "-hpddm_dir", dir, sizeof(dir), NULL);
764: PetscSNPrintf(lib, sizeof(lib), "%s/libhpddm_petsc", dir);
765: PetscDLLibraryRetrieve(PETSC_COMM_SELF, lib, dlib, 1024, &found);
766: if (found) {
767: PetscDLLibraryAppend(PETSC_COMM_SELF, &PetscDLLibrariesLoaded, dlib);
768: } else SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_PLIB, "%s not found", lib);
769: PetscDLLibrarySym(PETSC_COMM_SELF, &PetscDLLibrariesLoaded, NULL, "PCHPDDM_Internal", (void**)&loadedSym);
770: }
771: if (!loadedSym) SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_PLIB, "PCHPDDM_Internal symbol not found in %s", lib);
772: PetscNewLog(pc, &data);
773: pc->data = data;
774: pc->ops->reset = PCReset_HPDDM;
775: pc->ops->destroy = PCDestroy_HPDDM;
776: pc->ops->setfromoptions = PCSetFromOptions_HPDDM;
777: pc->ops->setup = PCSetUp_HPDDM;
778: pc->ops->apply = PCApply_HPDDM;
779: pc->ops->view = PCView_HPDDM;
780: pc->ops->applytranspose = 0;
781: pc->ops->applysymmetricleft = 0;
782: pc->ops->applysymmetricright = 0;
783: PetscObjectComposeFunction((PetscObject)pc, "PCHPDDMSetAuxiliaryMat_C", PCHPDDMSetAuxiliaryMat_HPDDM);
784: PetscObjectComposeFunction((PetscObject)pc, "PCHPDDMSetCoarseCorrectionType_C", PCHPDDMSetCoarseCorrectionType_HPDDM);
785: PetscObjectComposeFunction((PetscObject)pc, "PCHPDDMGetCoarseCorrectionType_C", PCHPDDMGetCoarseCorrectionType_HPDDM);
786: return(0);
787: }