xref: /petsc/src/ksp/pc/impls/gasm/gasm.c (revision 21e3ffae2f3b73c0bd738cf6d0a809700fc04bb0)
1 /*
2   This file defines an "generalized" additive Schwarz preconditioner for any Mat implementation.
3   In this version each MPI rank may intersect multiple subdomains and any subdomain may
4   intersect multiple MPI ranks.  Intersections of subdomains with MPI ranks are called *local
5   subdomains*.
6 
7        N    - total number of distinct global subdomains          (set explicitly in PCGASMSetTotalSubdomains() or implicitly PCGASMSetSubdomains() and then calculated in PCSetUp_GASM())
8        n    - actual number of local subdomains on this rank (set in PCGASMSetSubdomains() or calculated in PCGASMSetTotalSubdomains())
9        nmax - maximum number of local subdomains per rank    (calculated in PCSetUp_GASM())
10 */
11 #include <petsc/private/pcimpl.h> /*I "petscpc.h" I*/
12 #include <petscdm.h>
13 
14 typedef struct {
15   PetscInt   N, n, nmax;
16   PetscInt   overlap;                /* overlap requested by user */
17   PCGASMType type;                   /* use reduced interpolation, restriction or both */
18   PetscBool  type_set;               /* if user set this value (so won't change it for symmetric problems) */
19   PetscBool  same_subdomain_solvers; /* flag indicating whether all local solvers are same */
20   PetscBool  sort_indices;           /* flag to sort subdomain indices */
21   PetscBool  user_subdomains;        /* whether the user set explicit subdomain index sets -- keep them on PCReset() */
22   PetscBool  dm_subdomains;          /* whether DM is allowed to define subdomains */
23   PetscBool  hierarchicalpartitioning;
24   IS        *ois;           /* index sets that define the outer (conceptually, overlapping) subdomains */
25   IS        *iis;           /* index sets that define the inner (conceptually, nonoverlapping) subdomains */
26   KSP       *ksp;           /* linear solvers for each subdomain */
27   Mat       *pmat;          /* subdomain block matrices */
28   Vec        gx, gy;        /* Merged work vectors */
29   Vec       *x, *y;         /* Split work vectors; storage aliases pieces of storage of the above merged vectors. */
30   VecScatter gorestriction; /* merged restriction to disjoint union of outer subdomains */
31   VecScatter girestriction; /* merged restriction to disjoint union of inner subdomains */
32   VecScatter pctoouter;
33   IS         permutationIS;
34   Mat        permutationP;
35   Mat        pcmat;
36   Vec        pcx, pcy;
37 } PC_GASM;
38 
39 static PetscErrorCode PCGASMComputeGlobalSubdomainNumbering_Private(PC pc, PetscInt **numbering, PetscInt **permutation)
40 {
41   PC_GASM *osm = (PC_GASM *)pc->data;
42   PetscInt i;
43 
44   PetscFunctionBegin;
45   /* Determine the number of globally-distinct subdomains and compute a global numbering for them. */
46   PetscCall(PetscMalloc2(osm->n, numbering, osm->n, permutation));
47   PetscCall(PetscObjectsListGetGlobalNumbering(PetscObjectComm((PetscObject)pc), osm->n, (PetscObject *)osm->iis, NULL, *numbering));
48   for (i = 0; i < osm->n; ++i) (*permutation)[i] = i;
49   PetscCall(PetscSortIntWithPermutation(osm->n, *numbering, *permutation));
50   PetscFunctionReturn(PETSC_SUCCESS);
51 }
52 
53 static PetscErrorCode PCGASMSubdomainView_Private(PC pc, PetscInt i, PetscViewer viewer)
54 {
55   PC_GASM        *osm = (PC_GASM *)pc->data;
56   PetscInt        j, nidx;
57   const PetscInt *idx;
58   PetscViewer     sviewer;
59   char           *cidx;
60 
61   PetscFunctionBegin;
62   PetscCheck(i >= 0 && i < osm->n, PetscObjectComm((PetscObject)viewer), PETSC_ERR_ARG_WRONG, "Invalid subdomain %" PetscInt_FMT ": must nonnegative and less than %" PetscInt_FMT, i, osm->n);
63   /* Inner subdomains. */
64   PetscCall(ISGetLocalSize(osm->iis[i], &nidx));
65   /*
66    No more than 15 characters per index plus a space.
67    PetscViewerStringSPrintf requires a string of size at least 2, so use (nidx+1) instead of nidx,
68    in case nidx == 0. That will take care of the space for the trailing '\0' as well.
69    For nidx == 0, the whole string 16 '\0'.
70    */
71 #define len 16 * (nidx + 1) + 1
72   PetscCall(PetscMalloc1(len, &cidx));
73   PetscCall(PetscViewerStringOpen(PETSC_COMM_SELF, cidx, len, &sviewer));
74 #undef len
75   PetscCall(ISGetIndices(osm->iis[i], &idx));
76   for (j = 0; j < nidx; ++j) PetscCall(PetscViewerStringSPrintf(sviewer, "%" PetscInt_FMT " ", idx[j]));
77   PetscCall(ISRestoreIndices(osm->iis[i], &idx));
78   PetscCall(PetscViewerDestroy(&sviewer));
79   PetscCall(PetscViewerASCIIPrintf(viewer, "Inner subdomain:\n"));
80   PetscCall(PetscViewerFlush(viewer));
81   PetscCall(PetscViewerASCIIPushSynchronized(viewer));
82   PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%s", cidx));
83   PetscCall(PetscViewerFlush(viewer));
84   PetscCall(PetscViewerASCIIPopSynchronized(viewer));
85   PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
86   PetscCall(PetscViewerFlush(viewer));
87   PetscCall(PetscFree(cidx));
88   /* Outer subdomains. */
89   PetscCall(ISGetLocalSize(osm->ois[i], &nidx));
90   /*
91    No more than 15 characters per index plus a space.
92    PetscViewerStringSPrintf requires a string of size at least 2, so use (nidx+1) instead of nidx,
93    in case nidx == 0. That will take care of the space for the trailing '\0' as well.
94    For nidx == 0, the whole string 16 '\0'.
95    */
96 #define len 16 * (nidx + 1) + 1
97   PetscCall(PetscMalloc1(len, &cidx));
98   PetscCall(PetscViewerStringOpen(PETSC_COMM_SELF, cidx, len, &sviewer));
99 #undef len
100   PetscCall(ISGetIndices(osm->ois[i], &idx));
101   for (j = 0; j < nidx; ++j) PetscCall(PetscViewerStringSPrintf(sviewer, "%" PetscInt_FMT " ", idx[j]));
102   PetscCall(PetscViewerDestroy(&sviewer));
103   PetscCall(ISRestoreIndices(osm->ois[i], &idx));
104   PetscCall(PetscViewerASCIIPrintf(viewer, "Outer subdomain:\n"));
105   PetscCall(PetscViewerFlush(viewer));
106   PetscCall(PetscViewerASCIIPushSynchronized(viewer));
107   PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%s", cidx));
108   PetscCall(PetscViewerFlush(viewer));
109   PetscCall(PetscViewerASCIIPopSynchronized(viewer));
110   PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
111   PetscCall(PetscViewerFlush(viewer));
112   PetscCall(PetscFree(cidx));
113   PetscFunctionReturn(PETSC_SUCCESS);
114 }
115 
116 static PetscErrorCode PCGASMPrintSubdomains(PC pc)
117 {
118   PC_GASM    *osm = (PC_GASM *)pc->data;
119   const char *prefix;
120   char        fname[PETSC_MAX_PATH_LEN + 1];
121   PetscInt    l, d, count;
122   PetscBool   found;
123   PetscViewer viewer, sviewer = NULL;
124   PetscInt   *numbering, *permutation; /* global numbering of locally-supported subdomains and the permutation from the local ordering */
125 
126   PetscFunctionBegin;
127   PetscCall(PCGetOptionsPrefix(pc, &prefix));
128   PetscCall(PetscOptionsHasName(NULL, prefix, "-pc_gasm_print_subdomains", &found));
129   if (!found) PetscFunctionReturn(PETSC_SUCCESS);
130   PetscCall(PetscOptionsGetString(NULL, prefix, "-pc_gasm_print_subdomains", fname, sizeof(fname), &found));
131   if (!found) PetscCall(PetscStrcpy(fname, "stdout"));
132   PetscCall(PetscViewerASCIIOpen(PetscObjectComm((PetscObject)pc), fname, &viewer));
133   /*
134    Make sure the viewer has a name. Otherwise this may cause a deadlock or other weird errors when creating a subcomm viewer:
135    the subcomm viewer will attempt to inherit the viewer's name, which, if not set, will be constructed collectively on the comm.
136   */
137   PetscCall(PetscObjectName((PetscObject)viewer));
138   l = 0;
139   PetscCall(PCGASMComputeGlobalSubdomainNumbering_Private(pc, &numbering, &permutation));
140   for (count = 0; count < osm->N; ++count) {
141     /* Now let subdomains go one at a time in the global numbering order and print their subdomain/solver info. */
142     if (l < osm->n) {
143       d = permutation[l]; /* d is the local number of the l-th smallest (in the global ordering) among the locally supported subdomains */
144       if (numbering[d] == count) {
145         PetscCall(PetscViewerGetSubViewer(viewer, ((PetscObject)osm->ois[d])->comm, &sviewer));
146         PetscCall(PCGASMSubdomainView_Private(pc, d, sviewer));
147         PetscCall(PetscViewerRestoreSubViewer(viewer, ((PetscObject)osm->ois[d])->comm, &sviewer));
148         ++l;
149       }
150     }
151     PetscCallMPI(MPI_Barrier(PetscObjectComm((PetscObject)pc)));
152   }
153   PetscCall(PetscFree2(numbering, permutation));
154   PetscCall(PetscViewerDestroy(&viewer));
155   PetscFunctionReturn(PETSC_SUCCESS);
156 }
157 
158 static PetscErrorCode PCView_GASM(PC pc, PetscViewer viewer)
159 {
160   PC_GASM    *osm = (PC_GASM *)pc->data;
161   const char *prefix;
162   PetscMPIInt rank, size;
163   PetscInt    bsz;
164   PetscBool   iascii, view_subdomains = PETSC_FALSE;
165   PetscViewer sviewer;
166   PetscInt    count, l;
167   char        overlap[256]     = "user-defined overlap";
168   char        gsubdomains[256] = "unknown total number of subdomains";
169   char        msubdomains[256] = "unknown max number of local subdomains";
170   PetscInt   *numbering, *permutation; /* global numbering of locally-supported subdomains and the permutation from the local ordering */
171 
172   PetscFunctionBegin;
173   PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)pc), &size));
174   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)pc), &rank));
175 
176   if (osm->overlap >= 0) PetscCall(PetscSNPrintf(overlap, sizeof(overlap), "requested amount of overlap = %" PetscInt_FMT, osm->overlap));
177   if (osm->N != PETSC_DETERMINE) PetscCall(PetscSNPrintf(gsubdomains, sizeof(gsubdomains), "total number of subdomains = %" PetscInt_FMT, osm->N));
178   if (osm->nmax != PETSC_DETERMINE) PetscCall(PetscSNPrintf(msubdomains, sizeof(msubdomains), "max number of local subdomains = %" PetscInt_FMT, osm->nmax));
179 
180   PetscCall(PCGetOptionsPrefix(pc, &prefix));
181   PetscCall(PetscOptionsGetBool(NULL, prefix, "-pc_gasm_view_subdomains", &view_subdomains, NULL));
182 
183   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
184   if (iascii) {
185     /*
186      Make sure the viewer has a name. Otherwise this may cause a deadlock when creating a subcomm viewer:
187      the subcomm viewer will attempt to inherit the viewer's name, which, if not set, will be constructed
188      collectively on the comm.
189      */
190     PetscCall(PetscObjectName((PetscObject)viewer));
191     PetscCall(PetscViewerASCIIPrintf(viewer, "  Restriction/interpolation type: %s\n", PCGASMTypes[osm->type]));
192     PetscCall(PetscViewerASCIIPrintf(viewer, "  %s\n", overlap));
193     PetscCall(PetscViewerASCIIPrintf(viewer, "  %s\n", gsubdomains));
194     PetscCall(PetscViewerASCIIPrintf(viewer, "  %s\n", msubdomains));
195     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
196     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  [%d|%d] number of locally-supported subdomains = %" PetscInt_FMT "\n", rank, size, osm->n));
197     PetscCall(PetscViewerFlush(viewer));
198     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
199     /* Cannot take advantage of osm->same_subdomain_solvers without a global numbering of subdomains. */
200     PetscCall(PetscViewerASCIIPrintf(viewer, "  Subdomain solver info is as follows:\n"));
201     PetscCall(PetscViewerASCIIPushTab(viewer));
202     PetscCall(PetscViewerASCIIPrintf(viewer, "  - - - - - - - - - - - - - - - - - -\n"));
203     /* Make sure that everybody waits for the banner to be printed. */
204     PetscCallMPI(MPI_Barrier(PetscObjectComm((PetscObject)viewer)));
205     /* Now let subdomains go one at a time in the global numbering order and print their subdomain/solver info. */
206     PetscCall(PCGASMComputeGlobalSubdomainNumbering_Private(pc, &numbering, &permutation));
207     l = 0;
208     for (count = 0; count < osm->N; ++count) {
209       PetscMPIInt srank, ssize;
210       if (l < osm->n) {
211         PetscInt d = permutation[l]; /* d is the local number of the l-th smallest (in the global ordering) among the locally supported subdomains */
212         if (numbering[d] == count) {
213           PetscCallMPI(MPI_Comm_size(((PetscObject)osm->ois[d])->comm, &ssize));
214           PetscCallMPI(MPI_Comm_rank(((PetscObject)osm->ois[d])->comm, &srank));
215           PetscCall(PetscViewerGetSubViewer(viewer, ((PetscObject)osm->ois[d])->comm, &sviewer));
216           PetscCall(ISGetLocalSize(osm->ois[d], &bsz));
217           PetscCall(PetscViewerASCIISynchronizedPrintf(sviewer, "  [%d|%d] (subcomm [%d|%d]) local subdomain number %" PetscInt_FMT ", local size = %" PetscInt_FMT "\n", rank, size, srank, ssize, d, bsz));
218           PetscCall(PetscViewerFlush(sviewer));
219           if (view_subdomains) PetscCall(PCGASMSubdomainView_Private(pc, d, sviewer));
220           if (!pc->setupcalled) {
221             PetscCall(PetscViewerASCIIPrintf(sviewer, "  Solver not set up yet: PCSetUp() not yet called\n"));
222           } else {
223             PetscCall(KSPView(osm->ksp[d], sviewer));
224           }
225           PetscCall(PetscViewerASCIIPrintf(sviewer, "  - - - - - - - - - - - - - - - - - -\n"));
226           PetscCall(PetscViewerFlush(sviewer));
227           PetscCall(PetscViewerRestoreSubViewer(viewer, ((PetscObject)osm->ois[d])->comm, &sviewer));
228           ++l;
229         }
230       }
231       PetscCallMPI(MPI_Barrier(PetscObjectComm((PetscObject)pc)));
232     }
233     PetscCall(PetscFree2(numbering, permutation));
234     PetscCall(PetscViewerASCIIPopTab(viewer));
235     PetscCall(PetscViewerFlush(viewer));
236     /* this line is needed to match the extra PetscViewerASCIIPushSynchronized() in PetscViewerGetSubViewer() */
237     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
238   }
239   PetscFunctionReturn(PETSC_SUCCESS);
240 }
241 
242 PETSC_INTERN PetscErrorCode PCGASMCreateLocalSubdomains(Mat A, PetscInt nloc, IS *iis[]);
243 
244 PetscErrorCode PCGASMSetHierarchicalPartitioning(PC pc)
245 {
246   PC_GASM        *osm = (PC_GASM *)pc->data;
247   MatPartitioning part;
248   MPI_Comm        comm;
249   PetscMPIInt     size;
250   PetscInt        nlocalsubdomains, fromrows_localsize;
251   IS              partitioning, fromrows, isn;
252   Vec             outervec;
253 
254   PetscFunctionBegin;
255   PetscCall(PetscObjectGetComm((PetscObject)pc, &comm));
256   PetscCallMPI(MPI_Comm_size(comm, &size));
257   /* we do not need a hierarchical partitioning when
258     * the total number of subdomains is consistent with
259     * the number of MPI tasks.
260     * For the following cases, we do not need to use HP
261     * */
262   if (osm->N == PETSC_DETERMINE || osm->N >= size || osm->N == 1) PetscFunctionReturn(PETSC_SUCCESS);
263   PetscCheck(size % osm->N == 0, PETSC_COMM_WORLD, PETSC_ERR_ARG_INCOMP, "have to specify the total number of subdomains %" PetscInt_FMT " to be a factor of the number of ranks %d ", osm->N, size);
264   nlocalsubdomains = size / osm->N;
265   osm->n           = 1;
266   PetscCall(MatPartitioningCreate(comm, &part));
267   PetscCall(MatPartitioningSetAdjacency(part, pc->pmat));
268   PetscCall(MatPartitioningSetType(part, MATPARTITIONINGHIERARCH));
269   PetscCall(MatPartitioningHierarchicalSetNcoarseparts(part, osm->N));
270   PetscCall(MatPartitioningHierarchicalSetNfineparts(part, nlocalsubdomains));
271   PetscCall(MatPartitioningSetFromOptions(part));
272   /* get new rank owner number of each vertex */
273   PetscCall(MatPartitioningApply(part, &partitioning));
274   PetscCall(ISBuildTwoSided(partitioning, NULL, &fromrows));
275   PetscCall(ISPartitioningToNumbering(partitioning, &isn));
276   PetscCall(ISDestroy(&isn));
277   PetscCall(ISGetLocalSize(fromrows, &fromrows_localsize));
278   PetscCall(MatPartitioningDestroy(&part));
279   PetscCall(MatCreateVecs(pc->pmat, &outervec, NULL));
280   PetscCall(VecCreateMPI(comm, fromrows_localsize, PETSC_DETERMINE, &(osm->pcx)));
281   PetscCall(VecDuplicate(osm->pcx, &(osm->pcy)));
282   PetscCall(VecScatterCreate(osm->pcx, NULL, outervec, fromrows, &(osm->pctoouter)));
283   PetscCall(MatCreateSubMatrix(pc->pmat, fromrows, fromrows, MAT_INITIAL_MATRIX, &(osm->permutationP)));
284   PetscCall(PetscObjectReference((PetscObject)fromrows));
285   osm->permutationIS = fromrows;
286   osm->pcmat         = pc->pmat;
287   PetscCall(PetscObjectReference((PetscObject)osm->permutationP));
288   pc->pmat = osm->permutationP;
289   PetscCall(VecDestroy(&outervec));
290   PetscCall(ISDestroy(&fromrows));
291   PetscCall(ISDestroy(&partitioning));
292   osm->n = PETSC_DETERMINE;
293   PetscFunctionReturn(PETSC_SUCCESS);
294 }
295 
296 static PetscErrorCode PCSetUp_GASM(PC pc)
297 {
298   PC_GASM        *osm = (PC_GASM *)pc->data;
299   PetscInt        i, nInnerIndices, nTotalInnerIndices;
300   PetscMPIInt     rank, size;
301   MatReuse        scall = MAT_REUSE_MATRIX;
302   KSP             ksp;
303   PC              subpc;
304   const char     *prefix, *pprefix;
305   Vec             x, y;
306   PetscInt        oni;   /* Number of indices in the i-th local outer subdomain.               */
307   const PetscInt *oidxi; /* Indices from the i-th subdomain local outer subdomain.             */
308   PetscInt        on;    /* Number of indices in the disjoint union of local outer subdomains. */
309   PetscInt       *oidx;  /* Indices in the disjoint union of local outer subdomains. */
310   IS              gois;  /* Disjoint union the global indices of outer subdomains.             */
311   IS              goid;  /* Identity IS of the size of the disjoint union of outer subdomains. */
312   PetscScalar    *gxarray, *gyarray;
313   PetscInt        gostart; /* Start of locally-owned indices in the vectors -- osm->gx,osm->gy -- over the disjoint union of outer subdomains. */
314   PetscInt        num_subdomains  = 0;
315   DM             *subdomain_dm    = NULL;
316   char          **subdomain_names = NULL;
317   PetscInt       *numbering;
318 
319   PetscFunctionBegin;
320   PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)pc), &size));
321   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)pc), &rank));
322   if (!pc->setupcalled) {
323     /* use a hierarchical partitioning */
324     if (osm->hierarchicalpartitioning) PetscCall(PCGASMSetHierarchicalPartitioning(pc));
325     if (osm->n == PETSC_DETERMINE) {
326       if (osm->N != PETSC_DETERMINE) {
327         /* No local subdomains given, but the desired number of total subdomains is known, so construct them accordingly. */
328         PetscCall(PCGASMCreateSubdomains(pc->pmat, osm->N, &osm->n, &osm->iis));
329       } else if (osm->dm_subdomains && pc->dm) {
330         /* try pc->dm next, if allowed */
331         PetscInt d;
332         IS      *inner_subdomain_is, *outer_subdomain_is;
333         PetscCall(DMCreateDomainDecomposition(pc->dm, &num_subdomains, &subdomain_names, &inner_subdomain_is, &outer_subdomain_is, &subdomain_dm));
334         if (num_subdomains) PetscCall(PCGASMSetSubdomains(pc, num_subdomains, inner_subdomain_is, outer_subdomain_is));
335         for (d = 0; d < num_subdomains; ++d) {
336           if (inner_subdomain_is) PetscCall(ISDestroy(&inner_subdomain_is[d]));
337           if (outer_subdomain_is) PetscCall(ISDestroy(&outer_subdomain_is[d]));
338         }
339         PetscCall(PetscFree(inner_subdomain_is));
340         PetscCall(PetscFree(outer_subdomain_is));
341       } else {
342         /* still no subdomains; use one per rank */
343         osm->nmax = osm->n = 1;
344         PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)pc), &size));
345         osm->N = size;
346         PetscCall(PCGASMCreateLocalSubdomains(pc->pmat, osm->n, &osm->iis));
347       }
348     }
349     if (!osm->iis) {
350       /*
351        osm->n was set in PCGASMSetSubdomains(), but the actual subdomains have not been supplied.
352        We create the requisite number of local inner subdomains and then expand them into
353        out subdomains, if necessary.
354        */
355       PetscCall(PCGASMCreateLocalSubdomains(pc->pmat, osm->n, &osm->iis));
356     }
357     if (!osm->ois) {
358       /*
359             Initially make outer subdomains the same as inner subdomains. If nonzero additional overlap
360             has been requested, copy the inner subdomains over so they can be modified.
361       */
362       PetscCall(PetscMalloc1(osm->n, &osm->ois));
363       for (i = 0; i < osm->n; ++i) {
364         if (osm->overlap > 0 && osm->N > 1) { /* With positive overlap, osm->iis[i] will be modified */
365           PetscCall(ISDuplicate(osm->iis[i], (osm->ois) + i));
366           PetscCall(ISCopy(osm->iis[i], osm->ois[i]));
367         } else {
368           PetscCall(PetscObjectReference((PetscObject)((osm->iis)[i])));
369           osm->ois[i] = osm->iis[i];
370         }
371       }
372       if (osm->overlap > 0 && osm->N > 1) {
373         /* Extend the "overlapping" regions by a number of steps */
374         PetscCall(MatIncreaseOverlapSplit(pc->pmat, osm->n, osm->ois, osm->overlap));
375       }
376     }
377 
378     /* Now the subdomains are defined.  Determine their global and max local numbers, if necessary. */
379     if (osm->nmax == PETSC_DETERMINE) {
380       PetscMPIInt inwork, outwork;
381       /* determine global number of subdomains and the max number of local subdomains */
382       inwork = osm->n;
383       PetscCall(MPIU_Allreduce(&inwork, &outwork, 1, MPI_INT, MPI_MAX, PetscObjectComm((PetscObject)pc)));
384       osm->nmax = outwork;
385     }
386     if (osm->N == PETSC_DETERMINE) {
387       /* Determine the number of globally-distinct subdomains and compute a global numbering for them. */
388       PetscCall(PetscObjectsListGetGlobalNumbering(PetscObjectComm((PetscObject)pc), osm->n, (PetscObject *)osm->ois, &osm->N, NULL));
389     }
390 
391     if (osm->sort_indices) {
392       for (i = 0; i < osm->n; i++) {
393         PetscCall(ISSort(osm->ois[i]));
394         PetscCall(ISSort(osm->iis[i]));
395       }
396     }
397     PetscCall(PCGetOptionsPrefix(pc, &prefix));
398     PetscCall(PCGASMPrintSubdomains(pc));
399 
400     /*
401        Merge the ISs, create merged vectors and restrictions.
402      */
403     /* Merge outer subdomain ISs and construct a restriction onto the disjoint union of local outer subdomains. */
404     on = 0;
405     for (i = 0; i < osm->n; i++) {
406       PetscCall(ISGetLocalSize(osm->ois[i], &oni));
407       on += oni;
408     }
409     PetscCall(PetscMalloc1(on, &oidx));
410     on = 0;
411     /* Merge local indices together */
412     for (i = 0; i < osm->n; i++) {
413       PetscCall(ISGetLocalSize(osm->ois[i], &oni));
414       PetscCall(ISGetIndices(osm->ois[i], &oidxi));
415       PetscCall(PetscArraycpy(oidx + on, oidxi, oni));
416       PetscCall(ISRestoreIndices(osm->ois[i], &oidxi));
417       on += oni;
418     }
419     PetscCall(ISCreateGeneral(((PetscObject)(pc))->comm, on, oidx, PETSC_OWN_POINTER, &gois));
420     nTotalInnerIndices = 0;
421     for (i = 0; i < osm->n; i++) {
422       PetscCall(ISGetLocalSize(osm->iis[i], &nInnerIndices));
423       nTotalInnerIndices += nInnerIndices;
424     }
425     PetscCall(VecCreateMPI(((PetscObject)(pc))->comm, nTotalInnerIndices, PETSC_DETERMINE, &x));
426     PetscCall(VecDuplicate(x, &y));
427 
428     PetscCall(VecCreateMPI(PetscObjectComm((PetscObject)pc), on, PETSC_DECIDE, &osm->gx));
429     PetscCall(VecDuplicate(osm->gx, &osm->gy));
430     PetscCall(VecGetOwnershipRange(osm->gx, &gostart, NULL));
431     PetscCall(ISCreateStride(PetscObjectComm((PetscObject)pc), on, gostart, 1, &goid));
432     /* gois might indices not on local */
433     PetscCall(VecScatterCreate(x, gois, osm->gx, goid, &(osm->gorestriction)));
434     PetscCall(PetscMalloc1(osm->n, &numbering));
435     PetscCall(PetscObjectsListGetGlobalNumbering(PetscObjectComm((PetscObject)pc), osm->n, (PetscObject *)osm->ois, NULL, numbering));
436     PetscCall(VecDestroy(&x));
437     PetscCall(ISDestroy(&gois));
438 
439     /* Merge inner subdomain ISs and construct a restriction onto the disjoint union of local inner subdomains. */
440     {
441       PetscInt        ini;   /* Number of indices the i-th a local inner subdomain. */
442       PetscInt        in;    /* Number of indices in the disjoint union of local inner subdomains. */
443       PetscInt       *iidx;  /* Global indices in the merged local inner subdomain. */
444       PetscInt       *ioidx; /* Global indices of the disjoint union of inner subdomains within the disjoint union of outer subdomains. */
445       IS              giis;  /* IS for the disjoint union of inner subdomains. */
446       IS              giois; /* IS for the disjoint union of inner subdomains within the disjoint union of outer subdomains. */
447       PetscScalar    *array;
448       const PetscInt *indices;
449       PetscInt        k;
450       on = 0;
451       for (i = 0; i < osm->n; i++) {
452         PetscCall(ISGetLocalSize(osm->ois[i], &oni));
453         on += oni;
454       }
455       PetscCall(PetscMalloc1(on, &iidx));
456       PetscCall(PetscMalloc1(on, &ioidx));
457       PetscCall(VecGetArray(y, &array));
458       /* set communicator id to determine where overlap is */
459       in = 0;
460       for (i = 0; i < osm->n; i++) {
461         PetscCall(ISGetLocalSize(osm->iis[i], &ini));
462         for (k = 0; k < ini; ++k) array[in + k] = numbering[i];
463         in += ini;
464       }
465       PetscCall(VecRestoreArray(y, &array));
466       PetscCall(VecScatterBegin(osm->gorestriction, y, osm->gy, INSERT_VALUES, SCATTER_FORWARD));
467       PetscCall(VecScatterEnd(osm->gorestriction, y, osm->gy, INSERT_VALUES, SCATTER_FORWARD));
468       PetscCall(VecGetOwnershipRange(osm->gy, &gostart, NULL));
469       PetscCall(VecGetArray(osm->gy, &array));
470       on = 0;
471       in = 0;
472       for (i = 0; i < osm->n; i++) {
473         PetscCall(ISGetLocalSize(osm->ois[i], &oni));
474         PetscCall(ISGetIndices(osm->ois[i], &indices));
475         for (k = 0; k < oni; k++) {
476           /*  skip overlapping indices to get inner domain */
477           if (PetscRealPart(array[on + k]) != numbering[i]) continue;
478           iidx[in]    = indices[k];
479           ioidx[in++] = gostart + on + k;
480         }
481         PetscCall(ISRestoreIndices(osm->ois[i], &indices));
482         on += oni;
483       }
484       PetscCall(VecRestoreArray(osm->gy, &array));
485       PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)pc), in, iidx, PETSC_OWN_POINTER, &giis));
486       PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)pc), in, ioidx, PETSC_OWN_POINTER, &giois));
487       PetscCall(VecScatterCreate(y, giis, osm->gy, giois, &osm->girestriction));
488       PetscCall(VecDestroy(&y));
489       PetscCall(ISDestroy(&giis));
490       PetscCall(ISDestroy(&giois));
491     }
492     PetscCall(ISDestroy(&goid));
493     PetscCall(PetscFree(numbering));
494 
495     /* Create the subdomain work vectors. */
496     PetscCall(PetscMalloc1(osm->n, &osm->x));
497     PetscCall(PetscMalloc1(osm->n, &osm->y));
498     PetscCall(VecGetArray(osm->gx, &gxarray));
499     PetscCall(VecGetArray(osm->gy, &gyarray));
500     for (i = 0, on = 0; i < osm->n; ++i, on += oni) {
501       PetscInt oNi;
502       PetscCall(ISGetLocalSize(osm->ois[i], &oni));
503       /* on a sub communicator */
504       PetscCall(ISGetSize(osm->ois[i], &oNi));
505       PetscCall(VecCreateMPIWithArray(((PetscObject)(osm->ois[i]))->comm, 1, oni, oNi, gxarray + on, &osm->x[i]));
506       PetscCall(VecCreateMPIWithArray(((PetscObject)(osm->ois[i]))->comm, 1, oni, oNi, gyarray + on, &osm->y[i]));
507     }
508     PetscCall(VecRestoreArray(osm->gx, &gxarray));
509     PetscCall(VecRestoreArray(osm->gy, &gyarray));
510     /* Create the subdomain solvers */
511     PetscCall(PetscMalloc1(osm->n, &osm->ksp));
512     for (i = 0; i < osm->n; i++) {
513       char subprefix[PETSC_MAX_PATH_LEN + 1];
514       PetscCall(KSPCreate(((PetscObject)(osm->ois[i]))->comm, &ksp));
515       PetscCall(KSPSetErrorIfNotConverged(ksp, pc->erroriffailure));
516       PetscCall(PetscObjectIncrementTabLevel((PetscObject)ksp, (PetscObject)pc, 1));
517       PetscCall(KSPSetType(ksp, KSPPREONLY));
518       PetscCall(KSPGetPC(ksp, &subpc)); /* Why do we need this here? */
519       if (subdomain_dm) {
520         PetscCall(KSPSetDM(ksp, subdomain_dm[i]));
521         PetscCall(DMDestroy(subdomain_dm + i));
522       }
523       PetscCall(PCGetOptionsPrefix(pc, &prefix));
524       PetscCall(KSPSetOptionsPrefix(ksp, prefix));
525       if (subdomain_names && subdomain_names[i]) {
526         PetscCall(PetscSNPrintf(subprefix, PETSC_MAX_PATH_LEN, "sub_%s_", subdomain_names[i]));
527         PetscCall(KSPAppendOptionsPrefix(ksp, subprefix));
528         PetscCall(PetscFree(subdomain_names[i]));
529       }
530       PetscCall(KSPAppendOptionsPrefix(ksp, "sub_"));
531       osm->ksp[i] = ksp;
532     }
533     PetscCall(PetscFree(subdomain_dm));
534     PetscCall(PetscFree(subdomain_names));
535     scall = MAT_INITIAL_MATRIX;
536   } else { /* if (pc->setupcalled) */
537     /*
538        Destroy the submatrices from the previous iteration
539     */
540     if (pc->flag == DIFFERENT_NONZERO_PATTERN) {
541       PetscCall(MatDestroyMatrices(osm->n, &osm->pmat));
542       scall = MAT_INITIAL_MATRIX;
543     }
544     if (osm->permutationIS) {
545       PetscCall(MatCreateSubMatrix(pc->pmat, osm->permutationIS, osm->permutationIS, scall, &osm->permutationP));
546       PetscCall(PetscObjectReference((PetscObject)osm->permutationP));
547       osm->pcmat = pc->pmat;
548       pc->pmat   = osm->permutationP;
549     }
550   }
551 
552   /*
553      Extract the submatrices.
554   */
555   if (size > 1) {
556     PetscCall(MatCreateSubMatricesMPI(pc->pmat, osm->n, osm->ois, osm->ois, scall, &osm->pmat));
557   } else {
558     PetscCall(MatCreateSubMatrices(pc->pmat, osm->n, osm->ois, osm->ois, scall, &osm->pmat));
559   }
560   if (scall == MAT_INITIAL_MATRIX) {
561     PetscCall(PetscObjectGetOptionsPrefix((PetscObject)pc->pmat, &pprefix));
562     for (i = 0; i < osm->n; i++) { PetscCall(PetscObjectSetOptionsPrefix((PetscObject)osm->pmat[i], pprefix)); }
563   }
564 
565   /* Return control to the user so that the submatrices can be modified (e.g., to apply
566      different boundary conditions for the submatrices than for the global problem) */
567   PetscCall(PCModifySubMatrices(pc, osm->n, osm->ois, osm->ois, osm->pmat, pc->modifysubmatricesP));
568 
569   /*
570      Loop over submatrices putting them into local ksps
571   */
572   for (i = 0; i < osm->n; i++) {
573     PetscCall(KSPSetOperators(osm->ksp[i], osm->pmat[i], osm->pmat[i]));
574     PetscCall(KSPGetOptionsPrefix(osm->ksp[i], &prefix));
575     PetscCall(MatSetOptionsPrefix(osm->pmat[i], prefix));
576     if (!pc->setupcalled) PetscCall(KSPSetFromOptions(osm->ksp[i]));
577   }
578   if (osm->pcmat) {
579     PetscCall(MatDestroy(&pc->pmat));
580     pc->pmat   = osm->pcmat;
581     osm->pcmat = NULL;
582   }
583   PetscFunctionReturn(PETSC_SUCCESS);
584 }
585 
586 static PetscErrorCode PCSetUpOnBlocks_GASM(PC pc)
587 {
588   PC_GASM *osm = (PC_GASM *)pc->data;
589   PetscInt i;
590 
591   PetscFunctionBegin;
592   for (i = 0; i < osm->n; i++) PetscCall(KSPSetUp(osm->ksp[i]));
593   PetscFunctionReturn(PETSC_SUCCESS);
594 }
595 
596 static PetscErrorCode PCApply_GASM(PC pc, Vec xin, Vec yout)
597 {
598   PC_GASM    *osm = (PC_GASM *)pc->data;
599   PetscInt    i;
600   Vec         x, y;
601   ScatterMode forward = SCATTER_FORWARD, reverse = SCATTER_REVERSE;
602 
603   PetscFunctionBegin;
604   if (osm->pctoouter) {
605     PetscCall(VecScatterBegin(osm->pctoouter, xin, osm->pcx, INSERT_VALUES, SCATTER_REVERSE));
606     PetscCall(VecScatterEnd(osm->pctoouter, xin, osm->pcx, INSERT_VALUES, SCATTER_REVERSE));
607     x = osm->pcx;
608     y = osm->pcy;
609   } else {
610     x = xin;
611     y = yout;
612   }
613   /*
614      support for limiting the restriction or interpolation only to the inner
615      subdomain values (leaving the other values 0).
616   */
617   if (!(osm->type & PC_GASM_RESTRICT)) {
618     /* have to zero the work RHS since scatter may leave some slots empty */
619     PetscCall(VecZeroEntries(osm->gx));
620     PetscCall(VecScatterBegin(osm->girestriction, x, osm->gx, INSERT_VALUES, forward));
621   } else {
622     PetscCall(VecScatterBegin(osm->gorestriction, x, osm->gx, INSERT_VALUES, forward));
623   }
624   PetscCall(VecZeroEntries(osm->gy));
625   if (!(osm->type & PC_GASM_RESTRICT)) {
626     PetscCall(VecScatterEnd(osm->girestriction, x, osm->gx, INSERT_VALUES, forward));
627   } else {
628     PetscCall(VecScatterEnd(osm->gorestriction, x, osm->gx, INSERT_VALUES, forward));
629   }
630   /* do the subdomain solves */
631   for (i = 0; i < osm->n; ++i) {
632     PetscCall(KSPSolve(osm->ksp[i], osm->x[i], osm->y[i]));
633     PetscCall(KSPCheckSolve(osm->ksp[i], pc, osm->y[i]));
634   }
635   /* do we need to zero y? */
636   PetscCall(VecZeroEntries(y));
637   if (!(osm->type & PC_GASM_INTERPOLATE)) {
638     PetscCall(VecScatterBegin(osm->girestriction, osm->gy, y, ADD_VALUES, reverse));
639     PetscCall(VecScatterEnd(osm->girestriction, osm->gy, y, ADD_VALUES, reverse));
640   } else {
641     PetscCall(VecScatterBegin(osm->gorestriction, osm->gy, y, ADD_VALUES, reverse));
642     PetscCall(VecScatterEnd(osm->gorestriction, osm->gy, y, ADD_VALUES, reverse));
643   }
644   if (osm->pctoouter) {
645     PetscCall(VecScatterBegin(osm->pctoouter, y, yout, INSERT_VALUES, SCATTER_FORWARD));
646     PetscCall(VecScatterEnd(osm->pctoouter, y, yout, INSERT_VALUES, SCATTER_FORWARD));
647   }
648   PetscFunctionReturn(PETSC_SUCCESS);
649 }
650 
651 static PetscErrorCode PCMatApply_GASM(PC pc, Mat Xin, Mat Yout)
652 {
653   PC_GASM    *osm = (PC_GASM *)pc->data;
654   Mat         X, Y, O = NULL, Z, W;
655   Vec         x, y;
656   PetscInt    i, m, M, N;
657   ScatterMode forward = SCATTER_FORWARD, reverse = SCATTER_REVERSE;
658 
659   PetscFunctionBegin;
660   PetscCheck(osm->n == 1, PetscObjectComm((PetscObject)pc), PETSC_ERR_SUP, "Not yet implemented");
661   PetscCall(MatGetSize(Xin, NULL, &N));
662   if (osm->pctoouter) {
663     PetscCall(VecGetLocalSize(osm->pcx, &m));
664     PetscCall(VecGetSize(osm->pcx, &M));
665     PetscCall(MatCreateDense(PetscObjectComm((PetscObject)osm->ois[0]), m, PETSC_DECIDE, M, N, NULL, &O));
666     for (i = 0; i < N; ++i) {
667       PetscCall(MatDenseGetColumnVecRead(Xin, i, &x));
668       PetscCall(MatDenseGetColumnVecWrite(O, i, &y));
669       PetscCall(VecScatterBegin(osm->pctoouter, x, y, INSERT_VALUES, SCATTER_REVERSE));
670       PetscCall(VecScatterEnd(osm->pctoouter, x, y, INSERT_VALUES, SCATTER_REVERSE));
671       PetscCall(MatDenseRestoreColumnVecWrite(O, i, &y));
672       PetscCall(MatDenseRestoreColumnVecRead(Xin, i, &x));
673     }
674     X = Y = O;
675   } else {
676     X = Xin;
677     Y = Yout;
678   }
679   /*
680      support for limiting the restriction or interpolation only to the inner
681      subdomain values (leaving the other values 0).
682   */
683   PetscCall(VecGetLocalSize(osm->x[0], &m));
684   PetscCall(VecGetSize(osm->x[0], &M));
685   PetscCall(MatCreateDense(PetscObjectComm((PetscObject)osm->ois[0]), m, PETSC_DECIDE, M, N, NULL, &Z));
686   for (i = 0; i < N; ++i) {
687     PetscCall(MatDenseGetColumnVecRead(X, i, &x));
688     PetscCall(MatDenseGetColumnVecWrite(Z, i, &y));
689     if (!(osm->type & PC_GASM_RESTRICT)) {
690       /* have to zero the work RHS since scatter may leave some slots empty */
691       PetscCall(VecZeroEntries(y));
692       PetscCall(VecScatterBegin(osm->girestriction, x, y, INSERT_VALUES, forward));
693       PetscCall(VecScatterEnd(osm->girestriction, x, y, INSERT_VALUES, forward));
694     } else {
695       PetscCall(VecScatterBegin(osm->gorestriction, x, y, INSERT_VALUES, forward));
696       PetscCall(VecScatterEnd(osm->gorestriction, x, y, INSERT_VALUES, forward));
697     }
698     PetscCall(MatDenseRestoreColumnVecWrite(Z, i, &y));
699     PetscCall(MatDenseRestoreColumnVecRead(X, i, &x));
700   }
701   PetscCall(MatCreateDense(PetscObjectComm((PetscObject)osm->ois[0]), m, PETSC_DECIDE, M, N, NULL, &W));
702   PetscCall(MatSetOption(Z, MAT_NO_OFF_PROC_ENTRIES, PETSC_TRUE));
703   PetscCall(MatAssemblyBegin(Z, MAT_FINAL_ASSEMBLY));
704   PetscCall(MatAssemblyEnd(Z, MAT_FINAL_ASSEMBLY));
705   /* do the subdomain solve */
706   PetscCall(KSPMatSolve(osm->ksp[0], Z, W));
707   PetscCall(KSPCheckSolve(osm->ksp[0], pc, NULL));
708   PetscCall(MatDestroy(&Z));
709   /* do we need to zero y? */
710   PetscCall(MatZeroEntries(Y));
711   for (i = 0; i < N; ++i) {
712     PetscCall(MatDenseGetColumnVecWrite(Y, i, &y));
713     PetscCall(MatDenseGetColumnVecRead(W, i, &x));
714     if (!(osm->type & PC_GASM_INTERPOLATE)) {
715       PetscCall(VecScatterBegin(osm->girestriction, x, y, ADD_VALUES, reverse));
716       PetscCall(VecScatterEnd(osm->girestriction, x, y, ADD_VALUES, reverse));
717     } else {
718       PetscCall(VecScatterBegin(osm->gorestriction, x, y, ADD_VALUES, reverse));
719       PetscCall(VecScatterEnd(osm->gorestriction, x, y, ADD_VALUES, reverse));
720     }
721     PetscCall(MatDenseRestoreColumnVecRead(W, i, &x));
722     if (osm->pctoouter) {
723       PetscCall(MatDenseGetColumnVecWrite(Yout, i, &x));
724       PetscCall(VecScatterBegin(osm->pctoouter, y, x, INSERT_VALUES, SCATTER_FORWARD));
725       PetscCall(VecScatterEnd(osm->pctoouter, y, x, INSERT_VALUES, SCATTER_FORWARD));
726       PetscCall(MatDenseRestoreColumnVecRead(Yout, i, &x));
727     }
728     PetscCall(MatDenseRestoreColumnVecWrite(Y, i, &y));
729   }
730   PetscCall(MatDestroy(&W));
731   PetscCall(MatDestroy(&O));
732   PetscFunctionReturn(PETSC_SUCCESS);
733 }
734 
735 static PetscErrorCode PCApplyTranspose_GASM(PC pc, Vec xin, Vec yout)
736 {
737   PC_GASM    *osm = (PC_GASM *)pc->data;
738   PetscInt    i;
739   Vec         x, y;
740   ScatterMode forward = SCATTER_FORWARD, reverse = SCATTER_REVERSE;
741 
742   PetscFunctionBegin;
743   if (osm->pctoouter) {
744     PetscCall(VecScatterBegin(osm->pctoouter, xin, osm->pcx, INSERT_VALUES, SCATTER_REVERSE));
745     PetscCall(VecScatterEnd(osm->pctoouter, xin, osm->pcx, INSERT_VALUES, SCATTER_REVERSE));
746     x = osm->pcx;
747     y = osm->pcy;
748   } else {
749     x = xin;
750     y = yout;
751   }
752   /*
753      Support for limiting the restriction or interpolation to only local
754      subdomain values (leaving the other values 0).
755 
756      Note: these are reversed from the PCApply_GASM() because we are applying the
757      transpose of the three terms
758   */
759   if (!(osm->type & PC_GASM_INTERPOLATE)) {
760     /* have to zero the work RHS since scatter may leave some slots empty */
761     PetscCall(VecZeroEntries(osm->gx));
762     PetscCall(VecScatterBegin(osm->girestriction, x, osm->gx, INSERT_VALUES, forward));
763   } else {
764     PetscCall(VecScatterBegin(osm->gorestriction, x, osm->gx, INSERT_VALUES, forward));
765   }
766   PetscCall(VecZeroEntries(osm->gy));
767   if (!(osm->type & PC_GASM_INTERPOLATE)) {
768     PetscCall(VecScatterEnd(osm->girestriction, x, osm->gx, INSERT_VALUES, forward));
769   } else {
770     PetscCall(VecScatterEnd(osm->gorestriction, x, osm->gx, INSERT_VALUES, forward));
771   }
772   /* do the local solves */
773   for (i = 0; i < osm->n; ++i) { /* Note that the solves are local, so we can go to osm->n, rather than osm->nmax. */
774     PetscCall(KSPSolveTranspose(osm->ksp[i], osm->x[i], osm->y[i]));
775     PetscCall(KSPCheckSolve(osm->ksp[i], pc, osm->y[i]));
776   }
777   PetscCall(VecZeroEntries(y));
778   if (!(osm->type & PC_GASM_RESTRICT)) {
779     PetscCall(VecScatterBegin(osm->girestriction, osm->gy, y, ADD_VALUES, reverse));
780     PetscCall(VecScatterEnd(osm->girestriction, osm->gy, y, ADD_VALUES, reverse));
781   } else {
782     PetscCall(VecScatterBegin(osm->gorestriction, osm->gy, y, ADD_VALUES, reverse));
783     PetscCall(VecScatterEnd(osm->gorestriction, osm->gy, y, ADD_VALUES, reverse));
784   }
785   if (osm->pctoouter) {
786     PetscCall(VecScatterBegin(osm->pctoouter, y, yout, INSERT_VALUES, SCATTER_FORWARD));
787     PetscCall(VecScatterEnd(osm->pctoouter, y, yout, INSERT_VALUES, SCATTER_FORWARD));
788   }
789   PetscFunctionReturn(PETSC_SUCCESS);
790 }
791 
792 static PetscErrorCode PCReset_GASM(PC pc)
793 {
794   PC_GASM *osm = (PC_GASM *)pc->data;
795   PetscInt i;
796 
797   PetscFunctionBegin;
798   if (osm->ksp) {
799     for (i = 0; i < osm->n; i++) PetscCall(KSPReset(osm->ksp[i]));
800   }
801   if (osm->pmat) {
802     if (osm->n > 0) {
803       PetscMPIInt size;
804       PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)pc), &size));
805       if (size > 1) {
806         /* osm->pmat is created by MatCreateSubMatricesMPI(), cannot use MatDestroySubMatrices() */
807         PetscCall(MatDestroyMatrices(osm->n, &osm->pmat));
808       } else {
809         PetscCall(MatDestroySubMatrices(osm->n, &osm->pmat));
810       }
811     }
812   }
813   if (osm->x) {
814     for (i = 0; i < osm->n; i++) {
815       PetscCall(VecDestroy(&osm->x[i]));
816       PetscCall(VecDestroy(&osm->y[i]));
817     }
818   }
819   PetscCall(VecDestroy(&osm->gx));
820   PetscCall(VecDestroy(&osm->gy));
821 
822   PetscCall(VecScatterDestroy(&osm->gorestriction));
823   PetscCall(VecScatterDestroy(&osm->girestriction));
824   if (!osm->user_subdomains) {
825     PetscCall(PCGASMDestroySubdomains(osm->n, &osm->ois, &osm->iis));
826     osm->N    = PETSC_DETERMINE;
827     osm->nmax = PETSC_DETERMINE;
828   }
829   if (osm->pctoouter) PetscCall(VecScatterDestroy(&(osm->pctoouter)));
830   if (osm->permutationIS) PetscCall(ISDestroy(&(osm->permutationIS)));
831   if (osm->pcx) PetscCall(VecDestroy(&(osm->pcx)));
832   if (osm->pcy) PetscCall(VecDestroy(&(osm->pcy)));
833   if (osm->permutationP) PetscCall(MatDestroy(&(osm->permutationP)));
834   if (osm->pcmat) PetscCall(MatDestroy(&osm->pcmat));
835   PetscFunctionReturn(PETSC_SUCCESS);
836 }
837 
838 static PetscErrorCode PCDestroy_GASM(PC pc)
839 {
840   PC_GASM *osm = (PC_GASM *)pc->data;
841   PetscInt i;
842 
843   PetscFunctionBegin;
844   PetscCall(PCReset_GASM(pc));
845   /* PCReset will not destroy subdomains, if user_subdomains is true. */
846   PetscCall(PCGASMDestroySubdomains(osm->n, &osm->ois, &osm->iis));
847   if (osm->ksp) {
848     for (i = 0; i < osm->n; i++) PetscCall(KSPDestroy(&osm->ksp[i]));
849     PetscCall(PetscFree(osm->ksp));
850   }
851   PetscCall(PetscFree(osm->x));
852   PetscCall(PetscFree(osm->y));
853   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGASMSetSubdomains_C", NULL));
854   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGASMSetOverlap_C", NULL));
855   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGASMSetType_C", NULL));
856   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGASMSetSortIndices_C", NULL));
857   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGASMGetSubKSP_C", NULL));
858   PetscCall(PetscFree(pc->data));
859   PetscFunctionReturn(PETSC_SUCCESS);
860 }
861 
862 static PetscErrorCode PCSetFromOptions_GASM(PC pc, PetscOptionItems *PetscOptionsObject)
863 {
864   PC_GASM   *osm = (PC_GASM *)pc->data;
865   PetscInt   blocks, ovl;
866   PetscBool  flg;
867   PCGASMType gasmtype;
868 
869   PetscFunctionBegin;
870   PetscOptionsHeadBegin(PetscOptionsObject, "Generalized additive Schwarz options");
871   PetscCall(PetscOptionsBool("-pc_gasm_use_dm_subdomains", "If subdomains aren't set, use DMCreateDomainDecomposition() to define subdomains.", "PCGASMSetUseDMSubdomains", osm->dm_subdomains, &osm->dm_subdomains, &flg));
872   PetscCall(PetscOptionsInt("-pc_gasm_total_subdomains", "Total number of subdomains across communicator", "PCGASMSetTotalSubdomains", osm->N, &blocks, &flg));
873   if (flg) PetscCall(PCGASMSetTotalSubdomains(pc, blocks));
874   PetscCall(PetscOptionsInt("-pc_gasm_overlap", "Number of overlapping degrees of freedom", "PCGASMSetOverlap", osm->overlap, &ovl, &flg));
875   if (flg) {
876     PetscCall(PCGASMSetOverlap(pc, ovl));
877     osm->dm_subdomains = PETSC_FALSE;
878   }
879   flg = PETSC_FALSE;
880   PetscCall(PetscOptionsEnum("-pc_gasm_type", "Type of restriction/extension", "PCGASMSetType", PCGASMTypes, (PetscEnum)osm->type, (PetscEnum *)&gasmtype, &flg));
881   if (flg) PetscCall(PCGASMSetType(pc, gasmtype));
882   PetscCall(PetscOptionsBool("-pc_gasm_use_hierachical_partitioning", "use hierarchical partitioning", NULL, osm->hierarchicalpartitioning, &osm->hierarchicalpartitioning, &flg));
883   PetscOptionsHeadEnd();
884   PetscFunctionReturn(PETSC_SUCCESS);
885 }
886 
887 /*------------------------------------------------------------------------------------*/
888 
889 /*@
890     PCGASMSetTotalSubdomains - sets the total number of subdomains to use across the communicator for `PCGASM`
891 
892     Logically Collective
893 
894     Input Parameters:
895 +   pc  - the preconditioner
896 -   N   - total number of subdomains
897 
898     Level: beginner
899 
900 .seealso: `PCGASM`, `PCGASMSetSubdomains()`, `PCGASMSetOverlap()`
901           `PCGASMCreateSubdomains2D()`
902 @*/
903 PetscErrorCode PCGASMSetTotalSubdomains(PC pc, PetscInt N)
904 {
905   PC_GASM    *osm = (PC_GASM *)pc->data;
906   PetscMPIInt size, rank;
907 
908   PetscFunctionBegin;
909   PetscCheck(N >= 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Total number of subdomains must be 1 or more, got N = %" PetscInt_FMT, N);
910   PetscCheck(!pc->setupcalled, PetscObjectComm((PetscObject)pc), PETSC_ERR_ARG_WRONGSTATE, "PCGASMSetTotalSubdomains() should be called before calling PCSetUp().");
911 
912   PetscCall(PCGASMDestroySubdomains(osm->n, &osm->iis, &osm->ois));
913   osm->ois = osm->iis = NULL;
914 
915   PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)pc), &size));
916   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)pc), &rank));
917   osm->N             = N;
918   osm->n             = PETSC_DETERMINE;
919   osm->nmax          = PETSC_DETERMINE;
920   osm->dm_subdomains = PETSC_FALSE;
921   PetscFunctionReturn(PETSC_SUCCESS);
922 }
923 
924 static PetscErrorCode PCGASMSetSubdomains_GASM(PC pc, PetscInt n, IS iis[], IS ois[])
925 {
926   PC_GASM *osm = (PC_GASM *)pc->data;
927   PetscInt i;
928 
929   PetscFunctionBegin;
930   PetscCheck(n >= 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Each MPI rank must have 1 or more subdomains, got n = %" PetscInt_FMT, n);
931   PetscCheck(!pc->setupcalled, PetscObjectComm((PetscObject)pc), PETSC_ERR_ARG_WRONGSTATE, "PCGASMSetSubdomains() should be called before calling PCSetUp().");
932 
933   PetscCall(PCGASMDestroySubdomains(osm->n, &osm->iis, &osm->ois));
934   osm->iis = osm->ois = NULL;
935   osm->n              = n;
936   osm->N              = PETSC_DETERMINE;
937   osm->nmax           = PETSC_DETERMINE;
938   if (ois) {
939     PetscCall(PetscMalloc1(n, &osm->ois));
940     for (i = 0; i < n; i++) {
941       PetscCall(PetscObjectReference((PetscObject)ois[i]));
942       osm->ois[i] = ois[i];
943     }
944     /*
945        Since the user set the outer subdomains, even if nontrivial overlap was requested via PCGASMSetOverlap(),
946        it will be ignored.  To avoid confusion later on (e.g., when viewing the PC), the overlap size is set to -1.
947     */
948     osm->overlap = -1;
949     /* inner subdomains must be provided  */
950     PetscCheck(iis, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "inner indices have to be provided ");
951   } /* end if */
952   if (iis) {
953     PetscCall(PetscMalloc1(n, &osm->iis));
954     for (i = 0; i < n; i++) {
955       PetscCall(PetscObjectReference((PetscObject)iis[i]));
956       osm->iis[i] = iis[i];
957     }
958     if (!ois) {
959       osm->ois = NULL;
960       /* if user does not provide outer indices, we will create the corresponding outer indices using  osm->overlap =1 in PCSetUp_GASM */
961     }
962   }
963   if (PetscDefined(USE_DEBUG)) {
964     PetscInt        j, rstart, rend, *covered, lsize;
965     const PetscInt *indices;
966     /* check if the inner indices cover and only cover the local portion of the preconditioning matrix */
967     PetscCall(MatGetOwnershipRange(pc->pmat, &rstart, &rend));
968     PetscCall(PetscCalloc1(rend - rstart, &covered));
969     /* check if the current MPI rank owns indices from others */
970     for (i = 0; i < n; i++) {
971       PetscCall(ISGetIndices(osm->iis[i], &indices));
972       PetscCall(ISGetLocalSize(osm->iis[i], &lsize));
973       for (j = 0; j < lsize; j++) {
974         PetscCheck(indices[j] >= rstart && indices[j] < rend, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "inner subdomains can not own an index %" PetscInt_FMT " from other ranks", indices[j]);
975         PetscCheck(covered[indices[j] - rstart] != 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "inner subdomains can not have an overlapping index %" PetscInt_FMT " ", indices[j]);
976         covered[indices[j] - rstart] = 1;
977       }
978       PetscCall(ISRestoreIndices(osm->iis[i], &indices));
979     }
980     /* check if we miss any indices */
981     for (i = rstart; i < rend; i++) PetscCheck(covered[i - rstart], PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "local entity %" PetscInt_FMT " was not covered by inner subdomains", i);
982     PetscCall(PetscFree(covered));
983   }
984   if (iis) osm->user_subdomains = PETSC_TRUE;
985   PetscFunctionReturn(PETSC_SUCCESS);
986 }
987 
988 static PetscErrorCode PCGASMSetOverlap_GASM(PC pc, PetscInt ovl)
989 {
990   PC_GASM *osm = (PC_GASM *)pc->data;
991 
992   PetscFunctionBegin;
993   PetscCheck(ovl >= 0, PetscObjectComm((PetscObject)pc), PETSC_ERR_ARG_OUTOFRANGE, "Negative overlap value requested");
994   PetscCheck(!pc->setupcalled || ovl == osm->overlap, PetscObjectComm((PetscObject)pc), PETSC_ERR_ARG_WRONGSTATE, "PCGASMSetOverlap() should be called before PCSetUp().");
995   if (!pc->setupcalled) osm->overlap = ovl;
996   PetscFunctionReturn(PETSC_SUCCESS);
997 }
998 
999 static PetscErrorCode PCGASMSetType_GASM(PC pc, PCGASMType type)
1000 {
1001   PC_GASM *osm = (PC_GASM *)pc->data;
1002 
1003   PetscFunctionBegin;
1004   osm->type     = type;
1005   osm->type_set = PETSC_TRUE;
1006   PetscFunctionReturn(PETSC_SUCCESS);
1007 }
1008 
1009 static PetscErrorCode PCGASMSetSortIndices_GASM(PC pc, PetscBool doSort)
1010 {
1011   PC_GASM *osm = (PC_GASM *)pc->data;
1012 
1013   PetscFunctionBegin;
1014   osm->sort_indices = doSort;
1015   PetscFunctionReturn(PETSC_SUCCESS);
1016 }
1017 
1018 /*
1019    FIXME: This routine might need to be modified now that multiple ranks per subdomain are allowed.
1020         In particular, it would upset the global subdomain number calculation.
1021 */
1022 static PetscErrorCode PCGASMGetSubKSP_GASM(PC pc, PetscInt *n, PetscInt *first, KSP **ksp)
1023 {
1024   PC_GASM *osm = (PC_GASM *)pc->data;
1025 
1026   PetscFunctionBegin;
1027   PetscCheck(osm->n >= 1, PetscObjectComm((PetscObject)pc), PETSC_ERR_ORDER, "Need to call PCSetUp() on PC (or KSPSetUp() on the outer KSP object) before calling here");
1028 
1029   if (n) *n = osm->n;
1030   if (first) {
1031     PetscCallMPI(MPI_Scan(&osm->n, first, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)pc)));
1032     *first -= osm->n;
1033   }
1034   if (ksp) {
1035     /* Assume that local solves are now different; not necessarily
1036        true, though!  This flag is used only for PCView_GASM() */
1037     *ksp                        = osm->ksp;
1038     osm->same_subdomain_solvers = PETSC_FALSE;
1039   }
1040   PetscFunctionReturn(PETSC_SUCCESS);
1041 } /* PCGASMGetSubKSP_GASM() */
1042 
1043 /*@C
1044     PCGASMSetSubdomains - Sets the subdomains for this MPI rank
1045     for the additive Schwarz preconditioner with multiple MPI ranks per subdomain, `PCGASM`
1046 
1047     Collective
1048 
1049     Input Parameters:
1050 +   pc  - the preconditioner object
1051 .   n   - the number of subdomains for this MPI rank
1052 .   iis - the index sets that define the inner subdomains (or NULL for PETSc to determine subdomains)
1053 -   ois - the index sets that define the outer subdomains (or NULL to use the same as iis, or to construct by expanding iis by the requested overlap)
1054 
1055     Notes:
1056     The `IS` indices use the parallel, global numbering of the vector entries.
1057     Inner subdomains are those where the correction is applied.
1058     Outer subdomains are those where the residual necessary to obtain the
1059     corrections is obtained (see `PCGASMType` for the use of inner/outer subdomains).
1060     Both inner and outer subdomains can extend over several MPI ranks.
1061     This rank's portion of a subdomain is known as a local subdomain.
1062 
1063     Inner subdomains can not overlap with each other, do not have any entities from remote ranks,
1064     and  have to cover the entire local subdomain owned by the current rank. The index sets on each
1065     rank should be ordered such that the ith local subdomain is connected to the ith remote subdomain
1066     on another MPI rank.
1067 
1068     By default the `PGASM` preconditioner uses 1 (local) subdomain per MPI rank.
1069 
1070     Level: advanced
1071 
1072 .seealso: `PCGASM`, `PCGASMSetOverlap()`, `PCGASMGetSubKSP()`,
1073           `PCGASMCreateSubdomains2D()`, `PCGASMGetSubdomains()`
1074 @*/
1075 PetscErrorCode PCGASMSetSubdomains(PC pc, PetscInt n, IS iis[], IS ois[])
1076 {
1077   PC_GASM *osm = (PC_GASM *)pc->data;
1078 
1079   PetscFunctionBegin;
1080   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
1081   PetscTryMethod(pc, "PCGASMSetSubdomains_C", (PC, PetscInt, IS[], IS[]), (pc, n, iis, ois));
1082   osm->dm_subdomains = PETSC_FALSE;
1083   PetscFunctionReturn(PETSC_SUCCESS);
1084 }
1085 
1086 /*@
1087     PCGASMSetOverlap - Sets the overlap between a pair of subdomains for the
1088     additive Schwarz preconditioner `PCGASM`.  Either all or no MPI ranks in the
1089     pc communicator must call this routine.
1090 
1091     Logically Collective
1092 
1093     Input Parameters:
1094 +   pc  - the preconditioner context
1095 -   ovl - the amount of overlap between subdomains (ovl >= 0, default value = 0)
1096 
1097     Options Database Key:
1098 .   -pc_gasm_overlap <overlap> - Sets overlap
1099 
1100     Notes:
1101     By default the `PCGASM` preconditioner uses 1 subdomain per rank.  To use
1102     multiple subdomain per perocessor or "straddling" subdomains that intersect
1103     multiple ranks use `PCGASMSetSubdomains()` (or option -pc_gasm_total_subdomains <n>).
1104 
1105     The overlap defaults to 0, so if one desires that no additional
1106     overlap be computed beyond what may have been set with a call to
1107     `PCGASMSetSubdomains()`, then ovl must be set to be 0.  In particular, if one does
1108     not explicitly set the subdomains in application code, then all overlap would be computed
1109     internally by PETSc, and using an overlap of 0 would result in an `PCGASM`
1110     variant that is equivalent to the block Jacobi preconditioner.
1111 
1112     One can define initial index sets with any overlap via
1113     `PCGASMSetSubdomains()`; the routine `PCGASMSetOverlap()` merely allows
1114     PETSc to extend that overlap further, if desired.
1115 
1116     Level: intermediate
1117 
1118 .seealso: `PCGASM`, `PCGASMSetSubdomains()`, `PCGASMGetSubKSP()`,
1119           `PCGASMCreateSubdomains2D()`, `PCGASMGetSubdomains()`
1120 @*/
1121 PetscErrorCode PCGASMSetOverlap(PC pc, PetscInt ovl)
1122 {
1123   PC_GASM *osm = (PC_GASM *)pc->data;
1124 
1125   PetscFunctionBegin;
1126   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
1127   PetscValidLogicalCollectiveInt(pc, ovl, 2);
1128   PetscTryMethod(pc, "PCGASMSetOverlap_C", (PC, PetscInt), (pc, ovl));
1129   osm->dm_subdomains = PETSC_FALSE;
1130   PetscFunctionReturn(PETSC_SUCCESS);
1131 }
1132 
1133 /*@
1134     PCGASMSetType - Sets the type of restriction and interpolation used
1135     for local problems in the `PCGASM` additive Schwarz method.
1136 
1137     Logically Collective
1138 
1139     Input Parameters:
1140 +   pc  - the preconditioner context
1141 -   type - variant of `PCGASM`, one of
1142 .vb
1143       `PC_GASM_BASIC`       - full interpolation and restriction
1144       `PC_GASM_RESTRICT`    - full restriction, local MPI rank interpolation
1145       `PC_GASM_INTERPOLATE` - full interpolation, local MPI rank restriction
1146       `PC_GASM_NONE`        - local MPI rank restriction and interpolation
1147 .ve
1148 
1149     Options Database Key:
1150 .   -pc_gasm_type [basic,restrict,interpolate,none] - Sets GASM type
1151 
1152     Level: intermediate
1153 
1154 .seealso: `PCGASM`, `PCGASMSetSubdomains()`, `PCGASMGetSubKSP()`,
1155           `PCGASMCreateSubdomains2D()`, `PCASM`, `PCASMSetType()`
1156 @*/
1157 PetscErrorCode PCGASMSetType(PC pc, PCGASMType type)
1158 {
1159   PetscFunctionBegin;
1160   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
1161   PetscValidLogicalCollectiveEnum(pc, type, 2);
1162   PetscTryMethod(pc, "PCGASMSetType_C", (PC, PCGASMType), (pc, type));
1163   PetscFunctionReturn(PETSC_SUCCESS);
1164 }
1165 
1166 /*@
1167     PCGASMSetSortIndices - Determines whether subdomain indices are sorted.
1168 
1169     Logically Collective
1170 
1171     Input Parameters:
1172 +   pc  - the preconditioner context
1173 -   doSort - sort the subdomain indices
1174 
1175     Level: intermediate
1176 
1177 .seealso: `PCGASM`, `PCGASMSetSubdomains()`, `PCGASMGetSubKSP()`,
1178           `PCGASMCreateSubdomains2D()`
1179 @*/
1180 PetscErrorCode PCGASMSetSortIndices(PC pc, PetscBool doSort)
1181 {
1182   PetscFunctionBegin;
1183   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
1184   PetscValidLogicalCollectiveBool(pc, doSort, 2);
1185   PetscTryMethod(pc, "PCGASMSetSortIndices_C", (PC, PetscBool), (pc, doSort));
1186   PetscFunctionReturn(PETSC_SUCCESS);
1187 }
1188 
1189 /*@C
1190    PCGASMGetSubKSP - Gets the local `KSP` contexts for all subdomains on
1191    this MPI rank.
1192 
1193    Collective iff first_local is requested
1194 
1195    Input Parameter:
1196 .  pc - the preconditioner context
1197 
1198    Output Parameters:
1199 +  n_local - the number of blocks on this MPI rank or NULL
1200 .  first_local - the global number of the first block on this rank or NULL,
1201                  all ranks must request or all must pass NULL
1202 -  ksp - the array of `KSP` contexts
1203 
1204    Note:
1205    After `PCGASMGetSubKSP()` the array of `KSP`es is not to be freed
1206 
1207    Currently for some matrix implementations only 1 block per MPI rank
1208    is supported.
1209 
1210    You must call `KSPSetUp()` before calling `PCGASMGetSubKSP()`.
1211 
1212    Level: advanced
1213 
1214 .seealso: `PCGASM`, `PCGASMSetSubdomains()`, `PCGASMSetOverlap()`,
1215           `PCGASMCreateSubdomains2D()`,
1216 @*/
1217 PetscErrorCode PCGASMGetSubKSP(PC pc, PetscInt *n_local, PetscInt *first_local, KSP *ksp[])
1218 {
1219   PetscFunctionBegin;
1220   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
1221   PetscUseMethod(pc, "PCGASMGetSubKSP_C", (PC, PetscInt *, PetscInt *, KSP **), (pc, n_local, first_local, ksp));
1222   PetscFunctionReturn(PETSC_SUCCESS);
1223 }
1224 
1225 /*MC
1226    PCGASM - Use the (restricted) additive Schwarz method, each block is (approximately) solved with
1227            its own `KSP` object on a subset of MPI ranks
1228 
1229    Options Database Keys:
1230 +  -pc_gasm_total_subdomains <n>  - Sets total number of local subdomains to be distributed among MPI ranks
1231 .  -pc_gasm_view_subdomains       - activates the printing of subdomain indices in `PCView()`, -ksp_view or -snes_view
1232 .  -pc_gasm_print_subdomains      - activates the printing of subdomain indices in `PCSetUp()`
1233 .  -pc_gasm_overlap <ovl>         - Sets overlap by which to (automatically) extend local subdomains
1234 -  -pc_gasm_type [basic,restrict,interpolate,none] - Sets `PCGASMType`
1235 
1236    Notes:
1237    To set options on the solvers for each block append -sub_ to all the `KSP`, and `PC`
1238    options database keys. For example, -sub_pc_type ilu -sub_pc_factor_levels 1 -sub_ksp_type preonly
1239 
1240    To set the options on the solvers separate for each block call `PCGASMGetSubKSP()`
1241    and set the options directly on the resulting `KSP` object (you can access its `PC`
1242    with `KSPGetPC())`
1243 
1244    Level: beginner
1245 
1246     References:
1247 +   * - M Dryja, OB Widlund, An additive variant of the Schwarz alternating method for the case of many subregions
1248      Courant Institute, New York University Technical report
1249 -   * - Barry Smith, Petter Bjorstad, and William Gropp, Domain Decompositions: Parallel Multilevel Methods for Elliptic Partial Differential Equations,
1250     Cambridge University Press.
1251 
1252 .seealso: `PCCreate()`, `PCSetType()`, `PCType`, `PC`, `PCASM`, `PCGASMType`, `PCGASMSetType()`,
1253           `PCBJACOBI`, `PCGASMGetSubKSP()`, `PCGASMSetSubdomains()`,
1254           `PCSetModifySubMatrices()`, `PCGASMSetOverlap()`, `PCGASMSetType()`
1255 M*/
1256 
1257 PETSC_EXTERN PetscErrorCode PCCreate_GASM(PC pc)
1258 {
1259   PC_GASM *osm;
1260 
1261   PetscFunctionBegin;
1262   PetscCall(PetscNew(&osm));
1263 
1264   osm->N                        = PETSC_DETERMINE;
1265   osm->n                        = PETSC_DECIDE;
1266   osm->nmax                     = PETSC_DETERMINE;
1267   osm->overlap                  = 0;
1268   osm->ksp                      = NULL;
1269   osm->gorestriction            = NULL;
1270   osm->girestriction            = NULL;
1271   osm->pctoouter                = NULL;
1272   osm->gx                       = NULL;
1273   osm->gy                       = NULL;
1274   osm->x                        = NULL;
1275   osm->y                        = NULL;
1276   osm->pcx                      = NULL;
1277   osm->pcy                      = NULL;
1278   osm->permutationIS            = NULL;
1279   osm->permutationP             = NULL;
1280   osm->pcmat                    = NULL;
1281   osm->ois                      = NULL;
1282   osm->iis                      = NULL;
1283   osm->pmat                     = NULL;
1284   osm->type                     = PC_GASM_RESTRICT;
1285   osm->same_subdomain_solvers   = PETSC_TRUE;
1286   osm->sort_indices             = PETSC_TRUE;
1287   osm->dm_subdomains            = PETSC_FALSE;
1288   osm->hierarchicalpartitioning = PETSC_FALSE;
1289 
1290   pc->data                 = (void *)osm;
1291   pc->ops->apply           = PCApply_GASM;
1292   pc->ops->matapply        = PCMatApply_GASM;
1293   pc->ops->applytranspose  = PCApplyTranspose_GASM;
1294   pc->ops->setup           = PCSetUp_GASM;
1295   pc->ops->reset           = PCReset_GASM;
1296   pc->ops->destroy         = PCDestroy_GASM;
1297   pc->ops->setfromoptions  = PCSetFromOptions_GASM;
1298   pc->ops->setuponblocks   = PCSetUpOnBlocks_GASM;
1299   pc->ops->view            = PCView_GASM;
1300   pc->ops->applyrichardson = NULL;
1301 
1302   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGASMSetSubdomains_C", PCGASMSetSubdomains_GASM));
1303   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGASMSetOverlap_C", PCGASMSetOverlap_GASM));
1304   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGASMSetType_C", PCGASMSetType_GASM));
1305   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGASMSetSortIndices_C", PCGASMSetSortIndices_GASM));
1306   PetscCall(PetscObjectComposeFunction((PetscObject)pc, "PCGASMGetSubKSP_C", PCGASMGetSubKSP_GASM));
1307   PetscFunctionReturn(PETSC_SUCCESS);
1308 }
1309 
1310 PetscErrorCode PCGASMCreateLocalSubdomains(Mat A, PetscInt nloc, IS *iis[])
1311 {
1312   MatPartitioning mpart;
1313   const char     *prefix;
1314   PetscInt        i, j, rstart, rend, bs;
1315   PetscBool       hasop, isbaij = PETSC_FALSE, foundpart = PETSC_FALSE;
1316   Mat             Ad = NULL, adj;
1317   IS              ispart, isnumb, *is;
1318 
1319   PetscFunctionBegin;
1320   PetscCheck(nloc >= 1, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "number of local subdomains must > 0, got nloc = %" PetscInt_FMT, nloc);
1321 
1322   /* Get prefix, row distribution, and block size */
1323   PetscCall(MatGetOptionsPrefix(A, &prefix));
1324   PetscCall(MatGetOwnershipRange(A, &rstart, &rend));
1325   PetscCall(MatGetBlockSize(A, &bs));
1326   PetscCheck(rstart / bs * bs == rstart && rend / bs * bs == rend, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "bad row distribution [%" PetscInt_FMT ",%" PetscInt_FMT ") for matrix block size %" PetscInt_FMT, rstart, rend, bs);
1327 
1328   /* Get diagonal block from matrix if possible */
1329   PetscCall(MatHasOperation(A, MATOP_GET_DIAGONAL_BLOCK, &hasop));
1330   if (hasop) PetscCall(MatGetDiagonalBlock(A, &Ad));
1331   if (Ad) {
1332     PetscCall(PetscObjectBaseTypeCompare((PetscObject)Ad, MATSEQBAIJ, &isbaij));
1333     if (!isbaij) PetscCall(PetscObjectBaseTypeCompare((PetscObject)Ad, MATSEQSBAIJ, &isbaij));
1334   }
1335   if (Ad && nloc > 1) {
1336     PetscBool match, done;
1337     /* Try to setup a good matrix partitioning if available */
1338     PetscCall(MatPartitioningCreate(PETSC_COMM_SELF, &mpart));
1339     PetscCall(PetscObjectSetOptionsPrefix((PetscObject)mpart, prefix));
1340     PetscCall(MatPartitioningSetFromOptions(mpart));
1341     PetscCall(PetscObjectTypeCompare((PetscObject)mpart, MATPARTITIONINGCURRENT, &match));
1342     if (!match) PetscCall(PetscObjectTypeCompare((PetscObject)mpart, MATPARTITIONINGSQUARE, &match));
1343     if (!match) { /* assume a "good" partitioner is available */
1344       PetscInt        na;
1345       const PetscInt *ia, *ja;
1346       PetscCall(MatGetRowIJ(Ad, 0, PETSC_TRUE, isbaij, &na, &ia, &ja, &done));
1347       if (done) {
1348         /* Build adjacency matrix by hand. Unfortunately a call to
1349            MatConvert(Ad,MATMPIADJ,MAT_INITIAL_MATRIX,&adj) will
1350            remove the block-aij structure and we cannot expect
1351            MatPartitioning to split vertices as we need */
1352         PetscInt        i, j, len, nnz, cnt, *iia = NULL, *jja = NULL;
1353         const PetscInt *row;
1354         nnz = 0;
1355         for (i = 0; i < na; i++) { /* count number of nonzeros */
1356           len = ia[i + 1] - ia[i];
1357           row = ja + ia[i];
1358           for (j = 0; j < len; j++) {
1359             if (row[j] == i) { /* don't count diagonal */
1360               len--;
1361               break;
1362             }
1363           }
1364           nnz += len;
1365         }
1366         PetscCall(PetscMalloc1(na + 1, &iia));
1367         PetscCall(PetscMalloc1(nnz, &jja));
1368         nnz    = 0;
1369         iia[0] = 0;
1370         for (i = 0; i < na; i++) { /* fill adjacency */
1371           cnt = 0;
1372           len = ia[i + 1] - ia[i];
1373           row = ja + ia[i];
1374           for (j = 0; j < len; j++) {
1375             if (row[j] != i) jja[nnz + cnt++] = row[j]; /* if not diagonal */
1376           }
1377           nnz += cnt;
1378           iia[i + 1] = nnz;
1379         }
1380         /* Partitioning of the adjacency matrix */
1381         PetscCall(MatCreateMPIAdj(PETSC_COMM_SELF, na, na, iia, jja, NULL, &adj));
1382         PetscCall(MatPartitioningSetAdjacency(mpart, adj));
1383         PetscCall(MatPartitioningSetNParts(mpart, nloc));
1384         PetscCall(MatPartitioningApply(mpart, &ispart));
1385         PetscCall(ISPartitioningToNumbering(ispart, &isnumb));
1386         PetscCall(MatDestroy(&adj));
1387         foundpart = PETSC_TRUE;
1388       }
1389       PetscCall(MatRestoreRowIJ(Ad, 0, PETSC_TRUE, isbaij, &na, &ia, &ja, &done));
1390     }
1391     PetscCall(MatPartitioningDestroy(&mpart));
1392   }
1393   PetscCall(PetscMalloc1(nloc, &is));
1394   if (!foundpart) {
1395     /* Partitioning by contiguous chunks of rows */
1396 
1397     PetscInt mbs   = (rend - rstart) / bs;
1398     PetscInt start = rstart;
1399     for (i = 0; i < nloc; i++) {
1400       PetscInt count = (mbs / nloc + ((mbs % nloc) > i)) * bs;
1401       PetscCall(ISCreateStride(PETSC_COMM_SELF, count, start, 1, &is[i]));
1402       start += count;
1403     }
1404 
1405   } else {
1406     /* Partitioning by adjacency of diagonal block  */
1407 
1408     const PetscInt *numbering;
1409     PetscInt       *count, nidx, *indices, *newidx, start = 0;
1410     /* Get node count in each partition */
1411     PetscCall(PetscMalloc1(nloc, &count));
1412     PetscCall(ISPartitioningCount(ispart, nloc, count));
1413     if (isbaij && bs > 1) { /* adjust for the block-aij case */
1414       for (i = 0; i < nloc; i++) count[i] *= bs;
1415     }
1416     /* Build indices from node numbering */
1417     PetscCall(ISGetLocalSize(isnumb, &nidx));
1418     PetscCall(PetscMalloc1(nidx, &indices));
1419     for (i = 0; i < nidx; i++) indices[i] = i; /* needs to be initialized */
1420     PetscCall(ISGetIndices(isnumb, &numbering));
1421     PetscCall(PetscSortIntWithPermutation(nidx, numbering, indices));
1422     PetscCall(ISRestoreIndices(isnumb, &numbering));
1423     if (isbaij && bs > 1) { /* adjust for the block-aij case */
1424       PetscCall(PetscMalloc1(nidx * bs, &newidx));
1425       for (i = 0; i < nidx; i++) {
1426         for (j = 0; j < bs; j++) newidx[i * bs + j] = indices[i] * bs + j;
1427       }
1428       PetscCall(PetscFree(indices));
1429       nidx *= bs;
1430       indices = newidx;
1431     }
1432     /* Shift to get global indices */
1433     for (i = 0; i < nidx; i++) indices[i] += rstart;
1434 
1435     /* Build the index sets for each block */
1436     for (i = 0; i < nloc; i++) {
1437       PetscCall(ISCreateGeneral(PETSC_COMM_SELF, count[i], &indices[start], PETSC_COPY_VALUES, &is[i]));
1438       PetscCall(ISSort(is[i]));
1439       start += count[i];
1440     }
1441 
1442     PetscCall(PetscFree(count));
1443     PetscCall(PetscFree(indices));
1444     PetscCall(ISDestroy(&isnumb));
1445     PetscCall(ISDestroy(&ispart));
1446   }
1447   *iis = is;
1448   PetscFunctionReturn(PETSC_SUCCESS);
1449 }
1450 
1451 PETSC_INTERN PetscErrorCode PCGASMCreateStraddlingSubdomains(Mat A, PetscInt N, PetscInt *n, IS *iis[])
1452 {
1453   PetscFunctionBegin;
1454   PetscCall(MatSubdomainsCreateCoalesce(A, N, n, iis));
1455   PetscFunctionReturn(PETSC_SUCCESS);
1456 }
1457 
1458 /*@C
1459    PCGASMCreateSubdomains - Creates n index sets defining n nonoverlapping subdomains for the `PCGASM` additive
1460    Schwarz preconditioner for a any problem based on its matrix.
1461 
1462    Collective
1463 
1464    Input Parameters:
1465 +  A       - The global matrix operator
1466 -  N       - the number of global subdomains requested
1467 
1468    Output Parameters:
1469 +  n   - the number of subdomains created on this MPI rank
1470 -  iis - the array of index sets defining the local inner subdomains (on which the correction is applied)
1471 
1472    Level: advanced
1473 
1474    Note:
1475    When N >= A's communicator size, each subdomain is local -- contained within a single MPI rank.
1476          When N < size, the subdomains are 'straddling' (rank boundaries) and are no longer local.
1477          The resulting subdomains can be use in `PCGASMSetSubdomains`(pc,n,iss,NULL).  The overlapping
1478          outer subdomains will be automatically generated from these according to the requested amount of
1479          overlap; this is currently supported only with local subdomains.
1480 
1481 .seealso: `PCGASM`, `PCGASMSetSubdomains()`, `PCGASMDestroySubdomains()`
1482 @*/
1483 PetscErrorCode PCGASMCreateSubdomains(Mat A, PetscInt N, PetscInt *n, IS *iis[])
1484 {
1485   PetscMPIInt size;
1486 
1487   PetscFunctionBegin;
1488   PetscValidHeaderSpecific(A, MAT_CLASSID, 1);
1489   PetscValidPointer(iis, 4);
1490 
1491   PetscCheck(N >= 1, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of subdomains must be > 0, N = %" PetscInt_FMT, N);
1492   PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)A), &size));
1493   if (N >= size) {
1494     *n = N / size + (N % size);
1495     PetscCall(PCGASMCreateLocalSubdomains(A, *n, iis));
1496   } else {
1497     PetscCall(PCGASMCreateStraddlingSubdomains(A, N, n, iis));
1498   }
1499   PetscFunctionReturn(PETSC_SUCCESS);
1500 }
1501 
1502 /*@C
1503    PCGASMDestroySubdomains - Destroys the index sets created with
1504    `PCGASMCreateSubdomains()` or `PCGASMCreateSubdomains2D()`. Should be
1505    called after setting subdomains with `PCGASMSetSubdomains()`.
1506 
1507    Collective
1508 
1509    Input Parameters:
1510 +  n   - the number of index sets
1511 .  iis - the array of inner subdomains,
1512 -  ois - the array of outer subdomains, can be NULL
1513 
1514    Level: intermediate
1515 
1516    Note:
1517    This is a convenience subroutine that walks each list,
1518    destroys each `IS` on the list, and then frees the list. At the end the
1519    list pointers are set to NULL.
1520 
1521 .seealso: `PCGASM`, `PCGASMCreateSubdomains()`, `PCGASMSetSubdomains()`
1522 @*/
1523 PetscErrorCode PCGASMDestroySubdomains(PetscInt n, IS **iis, IS **ois)
1524 {
1525   PetscInt i;
1526 
1527   PetscFunctionBegin;
1528   if (n <= 0) PetscFunctionReturn(PETSC_SUCCESS);
1529   if (ois) {
1530     PetscValidPointer(ois, 3);
1531     if (*ois) {
1532       PetscValidPointer(*ois, 3);
1533       for (i = 0; i < n; i++) PetscCall(ISDestroy(&(*ois)[i]));
1534       PetscCall(PetscFree((*ois)));
1535     }
1536   }
1537   if (iis) {
1538     PetscValidPointer(iis, 2);
1539     if (*iis) {
1540       PetscValidPointer(*iis, 2);
1541       for (i = 0; i < n; i++) PetscCall(ISDestroy(&(*iis)[i]));
1542       PetscCall(PetscFree((*iis)));
1543     }
1544   }
1545   PetscFunctionReturn(PETSC_SUCCESS);
1546 }
1547 
1548 #define PCGASMLocalSubdomainBounds2D(M, N, xleft, ylow, xright, yhigh, first, last, xleft_loc, ylow_loc, xright_loc, yhigh_loc, n) \
1549   { \
1550     PetscInt first_row = first / M, last_row = last / M + 1; \
1551     /*                                                                                                    \
1552      Compute ylow_loc and yhigh_loc so that (ylow_loc,xleft) and (yhigh_loc,xright) are the corners       \
1553      of the bounding box of the intersection of the subdomain with the local ownership range (local       \
1554      subdomain).                                                                                          \
1555      Also compute xleft_loc and xright_loc as the lower and upper bounds on the first and last rows       \
1556      of the intersection.                                                                                 \
1557     */ \
1558     /* ylow_loc is the grid row containing the first element of the local sumbdomain */ \
1559     *ylow_loc = PetscMax(first_row, ylow); \
1560     /* xleft_loc is the offset of first element of the local subdomain within its grid row (might actually be outside the local subdomain) */ \
1561     *xleft_loc = *ylow_loc == first_row ? PetscMax(first % M, xleft) : xleft; \
1562     /* yhigh_loc is the grid row above the last local subdomain element */ \
1563     *yhigh_loc = PetscMin(last_row, yhigh); \
1564     /* xright is the offset of the end of the  local subdomain within its grid row (might actually be outside the local subdomain) */ \
1565     *xright_loc = *yhigh_loc == last_row ? PetscMin(xright, last % M) : xright; \
1566     /* Now compute the size of the local subdomain n. */ \
1567     *n = 0; \
1568     if (*ylow_loc < *yhigh_loc) { \
1569       PetscInt width = xright - xleft; \
1570       *n += width * (*yhigh_loc - *ylow_loc - 1); \
1571       *n += PetscMin(PetscMax(*xright_loc - xleft, 0), width); \
1572       *n -= PetscMin(PetscMax(*xleft_loc - xleft, 0), width); \
1573     } \
1574   }
1575 
1576 /*@
1577    PCGASMCreateSubdomains2D - Creates the index sets for the `PCGASM` overlapping Schwarz
1578    preconditioner for a two-dimensional problem on a regular grid.
1579 
1580    Collective
1581 
1582    Input Parameters:
1583 +  pc       - the preconditioner context
1584 .  M        - the global number of grid points in the x direction
1585 .  N        - the global number of grid points in the y direction
1586 .  Mdomains - the global number of subdomains in the x direction
1587 .  Ndomains - the global number of subdomains in the y direction
1588 .  dof      - degrees of freedom per node
1589 -  overlap  - overlap in mesh lines
1590 
1591    Output Parameters:
1592 +  Nsub - the number of local subdomains created
1593 .  iis  - array of index sets defining inner (nonoverlapping) subdomains
1594 -  ois  - array of index sets defining outer (overlapping, if overlap > 0) subdomains
1595 
1596    Level: advanced
1597 
1598 .seealso: `PCGASM`, `PCGASMSetSubdomains()`, `PCGASMGetSubKSP()`, `PCGASMSetOverlap()`
1599 @*/
1600 PetscErrorCode PCGASMCreateSubdomains2D(PC pc, PetscInt M, PetscInt N, PetscInt Mdomains, PetscInt Ndomains, PetscInt dof, PetscInt overlap, PetscInt *nsub, IS **iis, IS **ois)
1601 {
1602   PetscMPIInt size, rank;
1603   PetscInt    i, j;
1604   PetscInt    maxheight, maxwidth;
1605   PetscInt    xstart, xleft, xright, xleft_loc, xright_loc;
1606   PetscInt    ystart, ylow, yhigh, ylow_loc, yhigh_loc;
1607   PetscInt    x[2][2], y[2][2], n[2];
1608   PetscInt    first, last;
1609   PetscInt    nidx, *idx;
1610   PetscInt    ii, jj, s, q, d;
1611   PetscInt    k, kk;
1612   PetscMPIInt color;
1613   MPI_Comm    comm, subcomm;
1614   IS        **xis = NULL, **is = ois, **is_local = iis;
1615 
1616   PetscFunctionBegin;
1617   PetscCall(PetscObjectGetComm((PetscObject)pc, &comm));
1618   PetscCallMPI(MPI_Comm_size(comm, &size));
1619   PetscCallMPI(MPI_Comm_rank(comm, &rank));
1620   PetscCall(MatGetOwnershipRange(pc->pmat, &first, &last));
1621   PetscCheck((first % dof) == 0 && (last % dof) == 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE,
1622              "Matrix row partitioning unsuitable for domain decomposition: local row range (%" PetscInt_FMT ",%" PetscInt_FMT ") "
1623              "does not respect the number of degrees of freedom per grid point %" PetscInt_FMT,
1624              first, last, dof);
1625 
1626   /* Determine the number of domains with nonzero intersections with the local ownership range. */
1627   s      = 0;
1628   ystart = 0;
1629   for (j = 0; j < Ndomains; ++j) {
1630     maxheight = N / Ndomains + ((N % Ndomains) > j); /* Maximal height of subdomain */
1631     PetscCheck(maxheight >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Too many %" PetscInt_FMT " subdomains in the vertical direction for mesh height %" PetscInt_FMT, Ndomains, N);
1632     /* Vertical domain limits with an overlap. */
1633     ylow   = PetscMax(ystart - overlap, 0);
1634     yhigh  = PetscMin(ystart + maxheight + overlap, N);
1635     xstart = 0;
1636     for (i = 0; i < Mdomains; ++i) {
1637       maxwidth = M / Mdomains + ((M % Mdomains) > i); /* Maximal width of subdomain */
1638       PetscCheck(maxwidth >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Too many %" PetscInt_FMT " subdomains in the horizontal direction for mesh width %" PetscInt_FMT, Mdomains, M);
1639       /* Horizontal domain limits with an overlap. */
1640       xleft  = PetscMax(xstart - overlap, 0);
1641       xright = PetscMin(xstart + maxwidth + overlap, M);
1642       /*
1643          Determine whether this subdomain intersects this rank's ownership range of pc->pmat.
1644       */
1645       PCGASMLocalSubdomainBounds2D(M, N, xleft, ylow, xright, yhigh, first, last, (&xleft_loc), (&ylow_loc), (&xright_loc), (&yhigh_loc), (&nidx));
1646       if (nidx) ++s;
1647       xstart += maxwidth;
1648     } /* for (i = 0; i < Mdomains; ++i) */
1649     ystart += maxheight;
1650   } /* for (j = 0; j < Ndomains; ++j) */
1651 
1652   /* Now we can allocate the necessary number of ISs. */
1653   *nsub = s;
1654   PetscCall(PetscMalloc1(*nsub, is));
1655   PetscCall(PetscMalloc1(*nsub, is_local));
1656   s      = 0;
1657   ystart = 0;
1658   for (j = 0; j < Ndomains; ++j) {
1659     maxheight = N / Ndomains + ((N % Ndomains) > j); /* Maximal height of subdomain */
1660     PetscCheck(maxheight >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Too many %" PetscInt_FMT " subdomains in the vertical direction for mesh height %" PetscInt_FMT, Ndomains, N);
1661     /* Vertical domain limits with an overlap. */
1662     y[0][0] = PetscMax(ystart - overlap, 0);
1663     y[0][1] = PetscMin(ystart + maxheight + overlap, N);
1664     /* Vertical domain limits without an overlap. */
1665     y[1][0] = ystart;
1666     y[1][1] = PetscMin(ystart + maxheight, N);
1667     xstart  = 0;
1668     for (i = 0; i < Mdomains; ++i) {
1669       maxwidth = M / Mdomains + ((M % Mdomains) > i); /* Maximal width of subdomain */
1670       PetscCheck(maxwidth >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Too many %" PetscInt_FMT " subdomains in the horizontal direction for mesh width %" PetscInt_FMT, Mdomains, M);
1671       /* Horizontal domain limits with an overlap. */
1672       x[0][0] = PetscMax(xstart - overlap, 0);
1673       x[0][1] = PetscMin(xstart + maxwidth + overlap, M);
1674       /* Horizontal domain limits without an overlap. */
1675       x[1][0] = xstart;
1676       x[1][1] = PetscMin(xstart + maxwidth, M);
1677       /*
1678          Determine whether this domain intersects this rank's ownership range of pc->pmat.
1679          Do this twice: first for the domains with overlaps, and once without.
1680          During the first pass create the subcommunicators, and use them on the second pass as well.
1681       */
1682       for (q = 0; q < 2; ++q) {
1683         PetscBool split = PETSC_FALSE;
1684         /*
1685           domain limits, (xleft, xright) and (ylow, yheigh) are adjusted
1686           according to whether the domain with an overlap or without is considered.
1687         */
1688         xleft  = x[q][0];
1689         xright = x[q][1];
1690         ylow   = y[q][0];
1691         yhigh  = y[q][1];
1692         PCGASMLocalSubdomainBounds2D(M, N, xleft, ylow, xright, yhigh, first, last, (&xleft_loc), (&ylow_loc), (&xright_loc), (&yhigh_loc), (&nidx));
1693         nidx *= dof;
1694         n[q] = nidx;
1695         /*
1696          Based on the counted number of indices in the local domain *with an overlap*,
1697          construct a subcommunicator of all the MPI ranks supporting this domain.
1698          Observe that a domain with an overlap might have nontrivial local support,
1699          while the domain without an overlap might not.  Hence, the decision to participate
1700          in the subcommunicator must be based on the domain with an overlap.
1701          */
1702         if (q == 0) {
1703           if (nidx) color = 1;
1704           else color = MPI_UNDEFINED;
1705           PetscCallMPI(MPI_Comm_split(comm, color, rank, &subcomm));
1706           split = PETSC_TRUE;
1707         }
1708         /*
1709          Proceed only if the number of local indices *with an overlap* is nonzero.
1710          */
1711         if (n[0]) {
1712           if (q == 0) xis = is;
1713           if (q == 1) {
1714             /*
1715              The IS for the no-overlap subdomain shares a communicator with the overlapping domain.
1716              Moreover, if the overlap is zero, the two ISs are identical.
1717              */
1718             if (overlap == 0) {
1719               (*is_local)[s] = (*is)[s];
1720               PetscCall(PetscObjectReference((PetscObject)(*is)[s]));
1721               continue;
1722             } else {
1723               xis     = is_local;
1724               subcomm = ((PetscObject)(*is)[s])->comm;
1725             }
1726           } /* if (q == 1) */
1727           idx = NULL;
1728           PetscCall(PetscMalloc1(nidx, &idx));
1729           if (nidx) {
1730             k = 0;
1731             for (jj = ylow_loc; jj < yhigh_loc; ++jj) {
1732               PetscInt x0 = (jj == ylow_loc) ? xleft_loc : xleft;
1733               PetscInt x1 = (jj == yhigh_loc - 1) ? xright_loc : xright;
1734               kk          = dof * (M * jj + x0);
1735               for (ii = x0; ii < x1; ++ii) {
1736                 for (d = 0; d < dof; ++d) idx[k++] = kk++;
1737               }
1738             }
1739           }
1740           PetscCall(ISCreateGeneral(subcomm, nidx, idx, PETSC_OWN_POINTER, (*xis) + s));
1741           if (split) PetscCallMPI(MPI_Comm_free(&subcomm));
1742         } /* if (n[0]) */
1743       }   /* for (q = 0; q < 2; ++q) */
1744       if (n[0]) ++s;
1745       xstart += maxwidth;
1746     } /* for (i = 0; i < Mdomains; ++i) */
1747     ystart += maxheight;
1748   } /* for (j = 0; j < Ndomains; ++j) */
1749   PetscFunctionReturn(PETSC_SUCCESS);
1750 }
1751 
1752 /*@C
1753     PCGASMGetSubdomains - Gets the subdomains supported on this MPI rank
1754     for the `PCGASM` additive Schwarz preconditioner.
1755 
1756     Not Collective
1757 
1758     Input Parameter:
1759 .   pc - the preconditioner context
1760 
1761     Output Parameters:
1762 +   n   - the number of subdomains for this MPI rank (default value = 1)
1763 .   iis - the index sets that define the inner subdomains (without overlap) supported on this rank (can be NULL)
1764 -   ois - the index sets that define the outer subdomains (with overlap) supported on this rank (can be NULL)
1765 
1766     Note:
1767     The user is responsible for destroying the `IS`s and freeing the returned arrays.
1768     The `IS` numbering is in the parallel, global numbering of the vector.
1769 
1770     Level: advanced
1771 
1772 .seealso: `PCGASM`, `PCGASMSetOverlap()`, `PCGASMGetSubKSP()`, `PCGASMCreateSubdomains2D()`,
1773           `PCGASMSetSubdomains()`, `PCGASMGetSubmatrices()`
1774 @*/
1775 PetscErrorCode PCGASMGetSubdomains(PC pc, PetscInt *n, IS *iis[], IS *ois[])
1776 {
1777   PC_GASM  *osm;
1778   PetscBool match;
1779   PetscInt  i;
1780 
1781   PetscFunctionBegin;
1782   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
1783   PetscCall(PetscObjectTypeCompare((PetscObject)pc, PCGASM, &match));
1784   PetscCheck(match, PetscObjectComm((PetscObject)pc), PETSC_ERR_ARG_WRONG, "Incorrect object type: expected %s, got %s instead", PCGASM, ((PetscObject)pc)->type_name);
1785   osm = (PC_GASM *)pc->data;
1786   if (n) *n = osm->n;
1787   if (iis) PetscCall(PetscMalloc1(osm->n, iis));
1788   if (ois) PetscCall(PetscMalloc1(osm->n, ois));
1789   if (iis || ois) {
1790     for (i = 0; i < osm->n; ++i) {
1791       if (iis) (*iis)[i] = osm->iis[i];
1792       if (ois) (*ois)[i] = osm->ois[i];
1793     }
1794   }
1795   PetscFunctionReturn(PETSC_SUCCESS);
1796 }
1797 
1798 /*@C
1799     PCGASMGetSubmatrices - Gets the local submatrices (for this MPI rank
1800     only) for the `PCGASM` additive Schwarz preconditioner.
1801 
1802     Not Collective
1803 
1804     Input Parameter:
1805 .   pc - the preconditioner context
1806 
1807     Output Parameters:
1808 +   n   - the number of matrices for this MPI rank (default value = 1)
1809 -   mat - the matrices
1810 
1811     Note:
1812     Matrices returned by this routine have the same communicators as the index sets (IS)
1813            used to define subdomains in `PCGASMSetSubdomains()`
1814 
1815     Level: advanced
1816 
1817 .seealso: `PCGASM`, `PCGASMSetOverlap()`, `PCGASMGetSubKSP()`,
1818           `PCGASMCreateSubdomains2D()`, `PCGASMSetSubdomains()`, `PCGASMGetSubdomains()`
1819 @*/
1820 PetscErrorCode PCGASMGetSubmatrices(PC pc, PetscInt *n, Mat *mat[])
1821 {
1822   PC_GASM  *osm;
1823   PetscBool match;
1824 
1825   PetscFunctionBegin;
1826   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
1827   PetscValidIntPointer(n, 2);
1828   if (mat) PetscValidPointer(mat, 3);
1829   PetscCheck(pc->setupcalled, PetscObjectComm((PetscObject)pc), PETSC_ERR_ARG_WRONGSTATE, "Must call after KSPSetUp() or PCSetUp().");
1830   PetscCall(PetscObjectTypeCompare((PetscObject)pc, PCGASM, &match));
1831   PetscCheck(match, PetscObjectComm((PetscObject)pc), PETSC_ERR_ARG_WRONG, "Expected %s, got %s instead", PCGASM, ((PetscObject)pc)->type_name);
1832   osm = (PC_GASM *)pc->data;
1833   if (n) *n = osm->n;
1834   if (mat) *mat = osm->pmat;
1835   PetscFunctionReturn(PETSC_SUCCESS);
1836 }
1837 
1838 /*@
1839     PCGASMSetUseDMSubdomains - Indicates whether to use `DMCreateDomainDecomposition()` to define the subdomains, whenever possible for `PCGASM`
1840 
1841     Logically Collective
1842 
1843     Input Parameters:
1844 +   pc  - the preconditioner
1845 -   flg - boolean indicating whether to use subdomains defined by the `DM`
1846 
1847     Options Database Key:
1848 .   -pc_gasm_dm_subdomains -pc_gasm_overlap -pc_gasm_total_subdomains
1849 
1850     Level: intermediate
1851 
1852     Note:
1853     `PCGASMSetSubdomains()`, `PCGASMSetTotalSubdomains()` or `PCGASMSetOverlap()` take precedence over `PCGASMSetUseDMSubdomains()`,
1854     so setting `PCGASMSetSubdomains()` with nontrivial subdomain ISs or any of `PCGASMSetTotalSubdomains()` and `PCGASMSetOverlap()`
1855     automatically turns the latter off.
1856 
1857 .seealso: `PCGASM`, `PCGASMGetUseDMSubdomains()`, `PCGASMSetSubdomains()`, `PCGASMSetOverlap()`
1858           `PCGASMCreateSubdomains2D()`
1859 @*/
1860 PetscErrorCode PCGASMSetUseDMSubdomains(PC pc, PetscBool flg)
1861 {
1862   PC_GASM  *osm = (PC_GASM *)pc->data;
1863   PetscBool match;
1864 
1865   PetscFunctionBegin;
1866   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
1867   PetscValidLogicalCollectiveBool(pc, flg, 2);
1868   PetscCheck(!pc->setupcalled, ((PetscObject)pc)->comm, PETSC_ERR_ARG_WRONGSTATE, "Not for a setup PC.");
1869   PetscCall(PetscObjectTypeCompare((PetscObject)pc, PCGASM, &match));
1870   if (match) {
1871     if (!osm->user_subdomains && osm->N == PETSC_DETERMINE && osm->overlap < 0) osm->dm_subdomains = flg;
1872   }
1873   PetscFunctionReturn(PETSC_SUCCESS);
1874 }
1875 
1876 /*@
1877     PCGASMGetUseDMSubdomains - Returns flag indicating whether to use `DMCreateDomainDecomposition()` to define the subdomains, whenever possible with `PCGASM`
1878 
1879     Not Collective
1880 
1881     Input Parameter:
1882 .   pc  - the preconditioner
1883 
1884     Output Parameter:
1885 .   flg - boolean indicating whether to use subdomains defined by the `DM`
1886 
1887     Level: intermediate
1888 
1889 .seealso: `PCGASM`, `PCGASMSetUseDMSubdomains()`, `PCGASMSetOverlap()`
1890           `PCGASMCreateSubdomains2D()`
1891 @*/
1892 PetscErrorCode PCGASMGetUseDMSubdomains(PC pc, PetscBool *flg)
1893 {
1894   PC_GASM  *osm = (PC_GASM *)pc->data;
1895   PetscBool match;
1896 
1897   PetscFunctionBegin;
1898   PetscValidHeaderSpecific(pc, PC_CLASSID, 1);
1899   PetscValidBoolPointer(flg, 2);
1900   PetscCall(PetscObjectTypeCompare((PetscObject)pc, PCGASM, &match));
1901   if (match) {
1902     if (flg) *flg = osm->dm_subdomains;
1903   }
1904   PetscFunctionReturn(PETSC_SUCCESS);
1905 }
1906