xref: /petsc/src/ksp/pc/impls/patch/pcpatch.c (revision 9140fee14acbea959c6ed74f4836a1a89f708038)
1 #include <petsc/private/pcpatchimpl.h> /*I "petscpc.h" I*/
2 #include <petsc/private/kspimpl.h>     /* For ksp->setfromoptionscalled */
3 #include <petsc/private/vecimpl.h>     /* For vec->map */
4 #include <petsc/private/dmpleximpl.h>  /* For DMPlexComputeJacobian_Patch_Internal() */
5 #include <petscsf.h>
6 #include <petscbt.h>
7 #include <petscds.h>
8 #include <../src/mat/impls/dense/seq/dense.h> /*I "petscmat.h" I*/
9 
10 PetscBool  PCPatchcite       = PETSC_FALSE;
11 const char PCPatchCitation[] = "@article{FarrellKnepleyWechsungMitchell2020,\n"
12                                "title   = {{PCPATCH}: software for the topological construction of multigrid relaxation methods},\n"
13                                "author  = {Patrick E Farrell and Matthew G Knepley and Lawrence Mitchell and Florian Wechsung},\n"
14                                "journal = {ACM Transaction on Mathematical Software},\n"
15                                "eprint  = {http://arxiv.org/abs/1912.08516},\n"
16                                "volume  = {47},\n"
17                                "number  = {3},\n"
18                                "pages   = {1--22},\n"
19                                "year    = {2021},\n"
20                                "petsc_uses={KSP,DMPlex}\n}\n";
21 
22 PetscLogEvent PC_Patch_CreatePatches, PC_Patch_ComputeOp, PC_Patch_Solve, PC_Patch_Apply, PC_Patch_Prealloc;
23 
24 static inline PetscErrorCode ObjectView(PetscObject obj, PetscViewer viewer, PetscViewerFormat format)
25 {
26   PetscCall(PetscViewerPushFormat(viewer, format));
27   PetscCall(PetscObjectView(obj, viewer));
28   PetscCall(PetscViewerPopFormat(viewer));
29   return PETSC_SUCCESS;
30 }
31 
32 static PetscErrorCode PCPatchConstruct_Star(void *vpatch, DM dm, PetscInt point, PetscHSetI ht)
33 {
34   PetscInt  starSize;
35   PetscInt *star = NULL, si;
36 
37   PetscFunctionBegin;
38   PetscCall(PetscHSetIClear(ht));
39   /* To start with, add the point we care about */
40   PetscCall(PetscHSetIAdd(ht, point));
41   /* Loop over all the points that this point connects to */
42   PetscCall(DMPlexGetTransitiveClosure(dm, point, PETSC_FALSE, &starSize, &star));
43   for (si = 0; si < starSize * 2; si += 2) PetscCall(PetscHSetIAdd(ht, star[si]));
44   PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_FALSE, &starSize, &star));
45   PetscFunctionReturn(PETSC_SUCCESS);
46 }
47 
48 static PetscErrorCode PCPatchConstruct_Vanka(void *vpatch, DM dm, PetscInt point, PetscHSetI ht)
49 {
50   PC_PATCH *patch = (PC_PATCH *)vpatch;
51   PetscInt  starSize;
52   PetscInt *star         = NULL;
53   PetscBool shouldIgnore = PETSC_FALSE;
54   PetscInt  cStart, cEnd, iStart, iEnd, si;
55 
56   PetscFunctionBegin;
57   PetscCall(PetscHSetIClear(ht));
58   /* To start with, add the point we care about */
59   PetscCall(PetscHSetIAdd(ht, point));
60   /* Should we ignore any points of a certain dimension? */
61   if (patch->vankadim >= 0) {
62     shouldIgnore = PETSC_TRUE;
63     PetscCall(DMPlexGetDepthStratum(dm, patch->vankadim, &iStart, &iEnd));
64   }
65   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
66   /* Loop over all the cells that this point connects to */
67   PetscCall(DMPlexGetTransitiveClosure(dm, point, PETSC_FALSE, &starSize, &star));
68   for (si = 0; si < starSize * 2; si += 2) {
69     const PetscInt cell = star[si];
70     PetscInt       closureSize;
71     PetscInt      *closure = NULL, ci;
72 
73     if (cell < cStart || cell >= cEnd) continue;
74     /* now loop over all entities in the closure of that cell */
75     PetscCall(DMPlexGetTransitiveClosure(dm, cell, PETSC_TRUE, &closureSize, &closure));
76     for (ci = 0; ci < closureSize * 2; ci += 2) {
77       const PetscInt newpoint = closure[ci];
78 
79       /* We've been told to ignore entities of this type.*/
80       if (shouldIgnore && newpoint >= iStart && newpoint < iEnd) continue;
81       PetscCall(PetscHSetIAdd(ht, newpoint));
82     }
83     PetscCall(DMPlexRestoreTransitiveClosure(dm, cell, PETSC_TRUE, &closureSize, &closure));
84   }
85   PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_FALSE, &starSize, &star));
86   PetscFunctionReturn(PETSC_SUCCESS);
87 }
88 
89 static PetscErrorCode PCPatchConstruct_Pardecomp(void *vpatch, DM dm, PetscInt point, PetscHSetI ht)
90 {
91   PC_PATCH       *patch   = (PC_PATCH *)vpatch;
92   DMLabel         ghost   = NULL;
93   const PetscInt *leaves  = NULL;
94   PetscInt        nleaves = 0, pStart, pEnd, loc;
95   PetscBool       isFiredrake;
96   PetscBool       flg;
97   PetscInt        starSize;
98   PetscInt       *star = NULL;
99   PetscInt        opoint, overlapi;
100 
101   PetscFunctionBegin;
102   PetscCall(PetscHSetIClear(ht));
103 
104   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
105 
106   PetscCall(DMHasLabel(dm, "pyop2_ghost", &isFiredrake));
107   if (isFiredrake) {
108     PetscCall(DMGetLabel(dm, "pyop2_ghost", &ghost));
109     PetscCall(DMLabelCreateIndex(ghost, pStart, pEnd));
110   } else {
111     PetscSF sf;
112     PetscCall(DMGetPointSF(dm, &sf));
113     PetscCall(PetscSFGetGraph(sf, NULL, &nleaves, &leaves, NULL));
114     nleaves = PetscMax(nleaves, 0);
115   }
116 
117   for (opoint = pStart; opoint < pEnd; ++opoint) {
118     if (ghost) PetscCall(DMLabelHasPoint(ghost, opoint, &flg));
119     else {
120       PetscCall(PetscFindInt(opoint, nleaves, leaves, &loc));
121       flg = loc >= 0 ? PETSC_TRUE : PETSC_FALSE;
122     }
123     /* Not an owned entity, don't make a cell patch. */
124     if (flg) continue;
125     PetscCall(PetscHSetIAdd(ht, opoint));
126   }
127 
128   /* Now build the overlap for the patch */
129   for (overlapi = 0; overlapi < patch->pardecomp_overlap; ++overlapi) {
130     PetscInt  index    = 0;
131     PetscInt *htpoints = NULL;
132     PetscInt  htsize;
133     PetscInt  i;
134 
135     PetscCall(PetscHSetIGetSize(ht, &htsize));
136     PetscCall(PetscMalloc1(htsize, &htpoints));
137     PetscCall(PetscHSetIGetElems(ht, &index, htpoints));
138 
139     for (i = 0; i < htsize; ++i) {
140       PetscInt hpoint = htpoints[i];
141       PetscInt si;
142 
143       PetscCall(DMPlexGetTransitiveClosure(dm, hpoint, PETSC_FALSE, &starSize, &star));
144       for (si = 0; si < starSize * 2; si += 2) {
145         const PetscInt starp = star[si];
146         PetscInt       closureSize;
147         PetscInt      *closure = NULL, ci;
148 
149         /* now loop over all entities in the closure of starp */
150         PetscCall(DMPlexGetTransitiveClosure(dm, starp, PETSC_TRUE, &closureSize, &closure));
151         for (ci = 0; ci < closureSize * 2; ci += 2) {
152           const PetscInt closstarp = closure[ci];
153           PetscCall(PetscHSetIAdd(ht, closstarp));
154         }
155         PetscCall(DMPlexRestoreTransitiveClosure(dm, starp, PETSC_TRUE, &closureSize, &closure));
156       }
157       PetscCall(DMPlexRestoreTransitiveClosure(dm, hpoint, PETSC_FALSE, &starSize, &star));
158     }
159     PetscCall(PetscFree(htpoints));
160   }
161 
162   PetscFunctionReturn(PETSC_SUCCESS);
163 }
164 
165 /* The user's already set the patches in patch->userIS. Build the hash tables */
166 static PetscErrorCode PCPatchConstruct_User(void *vpatch, DM dm, PetscInt point, PetscHSetI ht)
167 {
168   PC_PATCH       *patch   = (PC_PATCH *)vpatch;
169   IS              patchis = patch->userIS[point];
170   PetscInt        n;
171   const PetscInt *patchdata;
172   PetscInt        pStart, pEnd, i;
173 
174   PetscFunctionBegin;
175   PetscCall(PetscHSetIClear(ht));
176   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
177   PetscCall(ISGetLocalSize(patchis, &n));
178   PetscCall(ISGetIndices(patchis, &patchdata));
179   for (i = 0; i < n; ++i) {
180     const PetscInt ownedpoint = patchdata[i];
181 
182     PetscCheck(ownedpoint >= pStart && ownedpoint < pEnd, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " was not in [%" PetscInt_FMT ", %" PetscInt_FMT ")", ownedpoint, pStart, pEnd);
183     PetscCall(PetscHSetIAdd(ht, ownedpoint));
184   }
185   PetscCall(ISRestoreIndices(patchis, &patchdata));
186   PetscFunctionReturn(PETSC_SUCCESS);
187 }
188 
189 static PetscErrorCode PCPatchCreateDefaultSF_Private(PC pc, PetscInt n, const PetscSF *sf, const PetscInt *bs)
190 {
191   PC_PATCH *patch = (PC_PATCH *)pc->data;
192   PetscInt  i;
193 
194   PetscFunctionBegin;
195   if (n == 1 && bs[0] == 1) {
196     patch->sectionSF = sf[0];
197     PetscCall(PetscObjectReference((PetscObject)patch->sectionSF));
198   } else {
199     PetscInt     allRoots = 0, allLeaves = 0;
200     PetscInt     leafOffset    = 0;
201     PetscInt    *ilocal        = NULL;
202     PetscSFNode *iremote       = NULL;
203     PetscInt    *remoteOffsets = NULL;
204     PetscInt     index         = 0;
205     PetscHMapI   rankToIndex;
206     PetscInt     numRanks = 0;
207     PetscSFNode *remote   = NULL;
208     PetscSF      rankSF;
209     PetscInt    *ranks   = NULL;
210     PetscInt    *offsets = NULL;
211     MPI_Datatype contig;
212     PetscHSetI   ranksUniq;
213 
214     /* First figure out how many dofs there are in the concatenated numbering.
215        allRoots: number of owned global dofs;
216        allLeaves: number of visible dofs (global + ghosted).
217     */
218     for (i = 0; i < n; ++i) {
219       PetscInt nroots, nleaves;
220 
221       PetscCall(PetscSFGetGraph(sf[i], &nroots, &nleaves, NULL, NULL));
222       allRoots += nroots * bs[i];
223       allLeaves += nleaves * bs[i];
224     }
225     PetscCall(PetscMalloc1(allLeaves, &ilocal));
226     PetscCall(PetscMalloc1(allLeaves, &iremote));
227     /* Now build an SF that just contains process connectivity. */
228     PetscCall(PetscHSetICreate(&ranksUniq));
229     for (i = 0; i < n; ++i) {
230       const PetscMPIInt *ranks = NULL;
231       PetscInt           nranks, j;
232 
233       PetscCall(PetscSFSetUp(sf[i]));
234       PetscCall(PetscSFGetRootRanks(sf[i], &nranks, &ranks, NULL, NULL, NULL));
235       /* These are all the ranks who communicate with me. */
236       for (j = 0; j < nranks; ++j) PetscCall(PetscHSetIAdd(ranksUniq, (PetscInt)ranks[j]));
237     }
238     PetscCall(PetscHSetIGetSize(ranksUniq, &numRanks));
239     PetscCall(PetscMalloc1(numRanks, &remote));
240     PetscCall(PetscMalloc1(numRanks, &ranks));
241     PetscCall(PetscHSetIGetElems(ranksUniq, &index, ranks));
242 
243     PetscCall(PetscHMapICreate(&rankToIndex));
244     for (i = 0; i < numRanks; ++i) {
245       remote[i].rank  = ranks[i];
246       remote[i].index = 0;
247       PetscCall(PetscHMapISet(rankToIndex, ranks[i], i));
248     }
249     PetscCall(PetscFree(ranks));
250     PetscCall(PetscHSetIDestroy(&ranksUniq));
251     PetscCall(PetscSFCreate(PetscObjectComm((PetscObject)pc), &rankSF));
252     PetscCall(PetscSFSetGraph(rankSF, 1, numRanks, NULL, PETSC_OWN_POINTER, remote, PETSC_OWN_POINTER));
253     PetscCall(PetscSFSetUp(rankSF));
254     /* OK, use it to communicate the root offset on the remote processes for each subspace. */
255     PetscCall(PetscMalloc1(n, &offsets));
256     PetscCall(PetscMalloc1(n * numRanks, &remoteOffsets));
257 
258     offsets[0] = 0;
259     for (i = 1; i < n; ++i) {
260       PetscInt nroots;
261 
262       PetscCall(PetscSFGetGraph(sf[i - 1], &nroots, NULL, NULL, NULL));
263       offsets[i] = offsets[i - 1] + nroots * bs[i - 1];
264     }
265     /* Offsets are the offsets on the current process of the global dof numbering for the subspaces. */
266     PetscCallMPI(MPI_Type_contiguous(n, MPIU_INT, &contig));
267     PetscCallMPI(MPI_Type_commit(&contig));
268 
269     PetscCall(PetscSFBcastBegin(rankSF, contig, offsets, remoteOffsets, MPI_REPLACE));
270     PetscCall(PetscSFBcastEnd(rankSF, contig, offsets, remoteOffsets, MPI_REPLACE));
271     PetscCallMPI(MPI_Type_free(&contig));
272     PetscCall(PetscFree(offsets));
273     PetscCall(PetscSFDestroy(&rankSF));
274     /* Now remoteOffsets contains the offsets on the remote
275       processes who communicate with me.  So now we can
276       concatenate the list of SFs into a single one. */
277     index = 0;
278     for (i = 0; i < n; ++i) {
279       const PetscSFNode *remote = NULL;
280       const PetscInt    *local  = NULL;
281       PetscInt           nroots, nleaves, j;
282 
283       PetscCall(PetscSFGetGraph(sf[i], &nroots, &nleaves, &local, &remote));
284       for (j = 0; j < nleaves; ++j) {
285         PetscInt rank = remote[j].rank;
286         PetscInt idx, rootOffset, k;
287 
288         PetscCall(PetscHMapIGet(rankToIndex, rank, &idx));
289         PetscCheck(idx != -1, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "Didn't find rank, huh?");
290         /* Offset on given rank for ith subspace */
291         rootOffset = remoteOffsets[n * idx + i];
292         for (k = 0; k < bs[i]; ++k) {
293           ilocal[index]        = (local ? local[j] : j) * bs[i] + k + leafOffset;
294           iremote[index].rank  = remote[j].rank;
295           iremote[index].index = remote[j].index * bs[i] + k + rootOffset;
296           ++index;
297         }
298       }
299       leafOffset += nleaves * bs[i];
300     }
301     PetscCall(PetscHMapIDestroy(&rankToIndex));
302     PetscCall(PetscFree(remoteOffsets));
303     PetscCall(PetscSFCreate(PetscObjectComm((PetscObject)pc), &patch->sectionSF));
304     PetscCall(PetscSFSetGraph(patch->sectionSF, allRoots, allLeaves, ilocal, PETSC_OWN_POINTER, iremote, PETSC_OWN_POINTER));
305   }
306   PetscFunctionReturn(PETSC_SUCCESS);
307 }
308 
309 /* TODO: Docs */
310 static PetscErrorCode PCPatchGetIgnoreDim(PC pc, PetscInt *dim)
311 {
312   PC_PATCH *patch = (PC_PATCH *)pc->data;
313   PetscFunctionBegin;
314   *dim = patch->ignoredim;
315   PetscFunctionReturn(PETSC_SUCCESS);
316 }
317 
318 /* TODO: Docs */
319 PetscErrorCode PCPatchSetSaveOperators(PC pc, PetscBool flg)
320 {
321   PC_PATCH *patch = (PC_PATCH *)pc->data;
322   PetscFunctionBegin;
323   patch->save_operators = flg;
324   PetscFunctionReturn(PETSC_SUCCESS);
325 }
326 
327 /* TODO: Docs */
328 PetscErrorCode PCPatchGetSaveOperators(PC pc, PetscBool *flg)
329 {
330   PC_PATCH *patch = (PC_PATCH *)pc->data;
331   PetscFunctionBegin;
332   *flg = patch->save_operators;
333   PetscFunctionReturn(PETSC_SUCCESS);
334 }
335 
336 /* TODO: Docs */
337 PetscErrorCode PCPatchSetPrecomputeElementTensors(PC pc, PetscBool flg)
338 {
339   PC_PATCH *patch = (PC_PATCH *)pc->data;
340   PetscFunctionBegin;
341   patch->precomputeElementTensors = flg;
342   PetscFunctionReturn(PETSC_SUCCESS);
343 }
344 
345 /* TODO: Docs */
346 PetscErrorCode PCPatchGetPrecomputeElementTensors(PC pc, PetscBool *flg)
347 {
348   PC_PATCH *patch = (PC_PATCH *)pc->data;
349   PetscFunctionBegin;
350   *flg = patch->precomputeElementTensors;
351   PetscFunctionReturn(PETSC_SUCCESS);
352 }
353 
354 /* TODO: Docs */
355 PetscErrorCode PCPatchSetPartitionOfUnity(PC pc, PetscBool flg)
356 {
357   PC_PATCH *patch = (PC_PATCH *)pc->data;
358   PetscFunctionBegin;
359   patch->partition_of_unity = flg;
360   PetscFunctionReturn(PETSC_SUCCESS);
361 }
362 
363 /* TODO: Docs */
364 PetscErrorCode PCPatchGetPartitionOfUnity(PC pc, PetscBool *flg)
365 {
366   PC_PATCH *patch = (PC_PATCH *)pc->data;
367   PetscFunctionBegin;
368   *flg = patch->partition_of_unity;
369   PetscFunctionReturn(PETSC_SUCCESS);
370 }
371 
372 /* TODO: Docs */
373 static PetscErrorCode PCPatchSetLocalComposition(PC pc, PCCompositeType type)
374 {
375   PC_PATCH *patch = (PC_PATCH *)pc->data;
376   PetscFunctionBegin;
377   PetscCheck(type == PC_COMPOSITE_ADDITIVE || type == PC_COMPOSITE_MULTIPLICATIVE, PetscObjectComm((PetscObject)pc), PETSC_ERR_SUP, "Only supports additive or multiplicative as the local type");
378   patch->local_composition_type = type;
379   PetscFunctionReturn(PETSC_SUCCESS);
380 }
381 
382 /* TODO: Docs */
383 PetscErrorCode PCPatchSetSubMatType(PC pc, MatType sub_mat_type)
384 {
385   PC_PATCH *patch = (PC_PATCH *)pc->data;
386 
387   PetscFunctionBegin;
388   if (patch->sub_mat_type) PetscCall(PetscFree(patch->sub_mat_type));
389   PetscCall(PetscStrallocpy(sub_mat_type, (char **)&patch->sub_mat_type));
390   PetscFunctionReturn(PETSC_SUCCESS);
391 }
392 
393 /* TODO: Docs */
394 PetscErrorCode PCPatchGetSubMatType(PC pc, MatType *sub_mat_type)
395 {
396   PC_PATCH *patch = (PC_PATCH *)pc->data;
397   PetscFunctionBegin;
398   *sub_mat_type = patch->sub_mat_type;
399   PetscFunctionReturn(PETSC_SUCCESS);
400 }
401 
402 /* TODO: Docs */
403 PetscErrorCode PCPatchSetCellNumbering(PC pc, PetscSection cellNumbering)
404 {
405   PC_PATCH *patch = (PC_PATCH *)pc->data;
406 
407   PetscFunctionBegin;
408   patch->cellNumbering = cellNumbering;
409   PetscCall(PetscObjectReference((PetscObject)cellNumbering));
410   PetscFunctionReturn(PETSC_SUCCESS);
411 }
412 
413 /* TODO: Docs */
414 PetscErrorCode PCPatchGetCellNumbering(PC pc, PetscSection *cellNumbering)
415 {
416   PC_PATCH *patch = (PC_PATCH *)pc->data;
417   PetscFunctionBegin;
418   *cellNumbering = patch->cellNumbering;
419   PetscFunctionReturn(PETSC_SUCCESS);
420 }
421 
422 /* TODO: Docs */
423 PetscErrorCode PCPatchSetConstructType(PC pc, PCPatchConstructType ctype, PetscErrorCode (*func)(PC, PetscInt *, IS **, IS *, void *), void *ctx)
424 {
425   PC_PATCH *patch = (PC_PATCH *)pc->data;
426 
427   PetscFunctionBegin;
428   patch->ctype = ctype;
429   switch (ctype) {
430   case PC_PATCH_STAR:
431     patch->user_patches     = PETSC_FALSE;
432     patch->patchconstructop = PCPatchConstruct_Star;
433     break;
434   case PC_PATCH_VANKA:
435     patch->user_patches     = PETSC_FALSE;
436     patch->patchconstructop = PCPatchConstruct_Vanka;
437     break;
438   case PC_PATCH_PARDECOMP:
439     patch->user_patches     = PETSC_FALSE;
440     patch->patchconstructop = PCPatchConstruct_Pardecomp;
441     break;
442   case PC_PATCH_USER:
443   case PC_PATCH_PYTHON:
444     patch->user_patches     = PETSC_TRUE;
445     patch->patchconstructop = PCPatchConstruct_User;
446     if (func) {
447       patch->userpatchconstructionop = func;
448       patch->userpatchconstructctx   = ctx;
449     }
450     break;
451   default:
452     SETERRQ(PetscObjectComm((PetscObject)pc), PETSC_ERR_USER, "Unknown patch construction type %" PetscInt_FMT, (PetscInt)patch->ctype);
453   }
454   PetscFunctionReturn(PETSC_SUCCESS);
455 }
456 
457 /* TODO: Docs */
458 PetscErrorCode PCPatchGetConstructType(PC pc, PCPatchConstructType *ctype, PetscErrorCode (**func)(PC, PetscInt *, IS **, IS *, void *), void **ctx)
459 {
460   PC_PATCH *patch = (PC_PATCH *)pc->data;
461 
462   PetscFunctionBegin;
463   *ctype = patch->ctype;
464   switch (patch->ctype) {
465   case PC_PATCH_STAR:
466   case PC_PATCH_VANKA:
467   case PC_PATCH_PARDECOMP:
468     break;
469   case PC_PATCH_USER:
470   case PC_PATCH_PYTHON:
471     *func = patch->userpatchconstructionop;
472     *ctx  = patch->userpatchconstructctx;
473     break;
474   default:
475     SETERRQ(PetscObjectComm((PetscObject)pc), PETSC_ERR_USER, "Unknown patch construction type %" PetscInt_FMT, (PetscInt)patch->ctype);
476   }
477   PetscFunctionReturn(PETSC_SUCCESS);
478 }
479 
480 /* TODO: Docs */
481 PetscErrorCode PCPatchSetDiscretisationInfo(PC pc, PetscInt nsubspaces, DM *dms, PetscInt *bs, PetscInt *nodesPerCell, const PetscInt **cellNodeMap, const PetscInt *subspaceOffsets, PetscInt numGhostBcs, const PetscInt *ghostBcNodes, PetscInt numGlobalBcs, const PetscInt *globalBcNodes)
482 {
483   PC_PATCH *patch = (PC_PATCH *)pc->data;
484   DM        dm, plex;
485   PetscSF  *sfs;
486   PetscInt  cStart, cEnd, i, j;
487 
488   PetscFunctionBegin;
489   PetscCall(PCGetDM(pc, &dm));
490   PetscCall(DMConvert(dm, DMPLEX, &plex));
491   dm = plex;
492   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
493   PetscCall(PetscMalloc1(nsubspaces, &sfs));
494   PetscCall(PetscMalloc1(nsubspaces, &patch->dofSection));
495   PetscCall(PetscMalloc1(nsubspaces, &patch->bs));
496   PetscCall(PetscMalloc1(nsubspaces, &patch->nodesPerCell));
497   PetscCall(PetscMalloc1(nsubspaces, &patch->cellNodeMap));
498   PetscCall(PetscMalloc1(nsubspaces + 1, &patch->subspaceOffsets));
499 
500   patch->nsubspaces       = nsubspaces;
501   patch->totalDofsPerCell = 0;
502   for (i = 0; i < nsubspaces; ++i) {
503     PetscCall(DMGetLocalSection(dms[i], &patch->dofSection[i]));
504     PetscCall(PetscObjectReference((PetscObject)patch->dofSection[i]));
505     PetscCall(DMGetSectionSF(dms[i], &sfs[i]));
506     patch->bs[i]           = bs[i];
507     patch->nodesPerCell[i] = nodesPerCell[i];
508     patch->totalDofsPerCell += nodesPerCell[i] * bs[i];
509     PetscCall(PetscMalloc1((cEnd - cStart) * nodesPerCell[i], &patch->cellNodeMap[i]));
510     for (j = 0; j < (cEnd - cStart) * nodesPerCell[i]; ++j) patch->cellNodeMap[i][j] = cellNodeMap[i][j];
511     patch->subspaceOffsets[i] = subspaceOffsets[i];
512   }
513   PetscCall(PCPatchCreateDefaultSF_Private(pc, nsubspaces, sfs, patch->bs));
514   PetscCall(PetscFree(sfs));
515 
516   patch->subspaceOffsets[nsubspaces] = subspaceOffsets[nsubspaces];
517   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, numGhostBcs, ghostBcNodes, PETSC_COPY_VALUES, &patch->ghostBcNodes));
518   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, numGlobalBcs, globalBcNodes, PETSC_COPY_VALUES, &patch->globalBcNodes));
519   PetscCall(DMDestroy(&dm));
520   PetscFunctionReturn(PETSC_SUCCESS);
521 }
522 
523 /* TODO: Docs */
524 static PetscErrorCode PCPatchSetDiscretisationInfoCombined(PC pc, DM dm, PetscInt *nodesPerCell, const PetscInt **cellNodeMap, PetscInt numGhostBcs, const PetscInt *ghostBcNodes, PetscInt numGlobalBcs, const PetscInt *globalBcNodes)
525 {
526   PC_PATCH *patch = (PC_PATCH *)pc->data;
527   PetscInt  cStart, cEnd, i, j;
528 
529   PetscFunctionBegin;
530   patch->combined = PETSC_TRUE;
531   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
532   PetscCall(DMGetNumFields(dm, &patch->nsubspaces));
533   PetscCall(PetscCalloc1(patch->nsubspaces, &patch->dofSection));
534   PetscCall(PetscMalloc1(patch->nsubspaces, &patch->bs));
535   PetscCall(PetscMalloc1(patch->nsubspaces, &patch->nodesPerCell));
536   PetscCall(PetscMalloc1(patch->nsubspaces, &patch->cellNodeMap));
537   PetscCall(PetscCalloc1(patch->nsubspaces + 1, &patch->subspaceOffsets));
538   PetscCall(DMGetLocalSection(dm, &patch->dofSection[0]));
539   PetscCall(PetscObjectReference((PetscObject)patch->dofSection[0]));
540   PetscCall(PetscSectionGetStorageSize(patch->dofSection[0], &patch->subspaceOffsets[patch->nsubspaces]));
541   patch->totalDofsPerCell = 0;
542   for (i = 0; i < patch->nsubspaces; ++i) {
543     patch->bs[i]           = 1;
544     patch->nodesPerCell[i] = nodesPerCell[i];
545     patch->totalDofsPerCell += nodesPerCell[i];
546     PetscCall(PetscMalloc1((cEnd - cStart) * nodesPerCell[i], &patch->cellNodeMap[i]));
547     for (j = 0; j < (cEnd - cStart) * nodesPerCell[i]; ++j) patch->cellNodeMap[i][j] = cellNodeMap[i][j];
548   }
549   PetscCall(DMGetSectionSF(dm, &patch->sectionSF));
550   PetscCall(PetscObjectReference((PetscObject)patch->sectionSF));
551   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, numGhostBcs, ghostBcNodes, PETSC_COPY_VALUES, &patch->ghostBcNodes));
552   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, numGlobalBcs, globalBcNodes, PETSC_COPY_VALUES, &patch->globalBcNodes));
553   PetscFunctionReturn(PETSC_SUCCESS);
554 }
555 
556 /*@C
557   PCPatchSetComputeFunction - Set the callback function used to compute patch residuals
558 
559   Logically Collective
560 
561   Input Parameters:
562 + pc   - The `PC`
563 . func - The callback function
564 - ctx  - The user context
565 
566   Calling sequence of `func`:
567 + pc               - The `PC`
568 . point            - The point
569 . x                - The input solution (not used in linear problems)
570 . f                - The patch residual vector
571 . cellIS           - An array of the cell numbers
572 . n                - The size of `dofsArray`
573 . dofsArray        - The dofmap for the dofs to be solved for
574 . dofsArrayWithAll - The dofmap for all dofs on the patch
575 - ctx              - The user context
576 
577   Level: advanced
578 
579   Note:
580   The entries of `f` (the output residual vector) have been set to zero before the call.
581 
582 .seealso: [](ch_ksp), `PCPatchSetComputeOperator()`, `PCPatchGetComputeOperator()`, `PCPatchSetDiscretisationInfo()`, `PCPatchSetComputeFunctionInteriorFacets()`
583 @*/
584 PetscErrorCode PCPatchSetComputeFunction(PC pc, PetscErrorCode (*func)(PC pc, PetscInt point, Vec x, Vec f, IS cellIS, PetscInt n, const PetscInt *dofsArray, const PetscInt *dofsArrayWithAll, void *ctx), void *ctx)
585 {
586   PC_PATCH *patch = (PC_PATCH *)pc->data;
587 
588   PetscFunctionBegin;
589   patch->usercomputef    = func;
590   patch->usercomputefctx = ctx;
591   PetscFunctionReturn(PETSC_SUCCESS);
592 }
593 
594 /*@C
595   PCPatchSetComputeFunctionInteriorFacets - Set the callback function used to compute facet integrals for patch residuals
596 
597   Logically Collective
598 
599   Input Parameters:
600 + pc   - The `PC`
601 . func - The callback function
602 - ctx  - The user context
603 
604   Calling sequence of `func`:
605 + pc               - The `PC`
606 . point            - The point
607 . x                - The input solution (not used in linear problems)
608 . f                - The patch residual vector
609 . facetIS          - An array of the facet numbers
610 . n                - The size of `dofsArray`
611 . dofsArray        - The dofmap for the dofs to be solved for
612 . dofsArrayWithAll - The dofmap for all dofs on the patch
613 - ctx              - The user context
614 
615   Level: advanced
616 
617   Note:
618   The entries of `f` (the output residual vector) have been set to zero before the call.
619 
620 .seealso: [](ch_ksp), `PCPatchSetComputeOperator()`, `PCPatchGetComputeOperator()`, `PCPatchSetDiscretisationInfo()`, `PCPatchSetComputeFunction()`
621 @*/
622 PetscErrorCode PCPatchSetComputeFunctionInteriorFacets(PC pc, PetscErrorCode (*func)(PC pc, PetscInt point, Vec x, Vec f, IS facetIS, PetscInt n, const PetscInt *dofsArray, const PetscInt *dofsArrayWithAll, void *ctx), void *ctx)
623 {
624   PC_PATCH *patch = (PC_PATCH *)pc->data;
625 
626   PetscFunctionBegin;
627   patch->usercomputefintfacet    = func;
628   patch->usercomputefintfacetctx = ctx;
629   PetscFunctionReturn(PETSC_SUCCESS);
630 }
631 
632 /*@C
633   PCPatchSetComputeOperator - Set the callback function used to compute patch matrices
634 
635   Logically Collective
636 
637   Input Parameters:
638 + pc   - The `PC`
639 . func - The callback function
640 - ctx  - The user context
641 
642   Calling sequence of `func`:
643 + pc               - The `PC`
644 . point            - The point
645 . x                - The input solution (not used in linear problems)
646 . mat              - The patch matrix
647 . facetIS          - An array of the cell numbers
648 . n                - The size of `dofsArray`
649 . dofsArray        - The dofmap for the dofs to be solved for
650 . dofsArrayWithAll - The dofmap for all dofs on the patch
651 - ctx              - The user context
652 
653   Level: advanced
654 
655   Note:
656   The matrix entries have been set to zero before the call.
657 
658 .seealso: [](ch_ksp), `PCPatchGetComputeOperator()`, `PCPatchSetComputeFunction()`, `PCPatchSetDiscretisationInfo()`
659 @*/
660 PetscErrorCode PCPatchSetComputeOperator(PC pc, PetscErrorCode (*func)(PC pc, PetscInt point, Vec x, Mat mat, IS facetIS, PetscInt n, const PetscInt *dofsArray, const PetscInt *dofsArrayWithAll, void *ctx), void *ctx)
661 {
662   PC_PATCH *patch = (PC_PATCH *)pc->data;
663 
664   PetscFunctionBegin;
665   patch->usercomputeop    = func;
666   patch->usercomputeopctx = ctx;
667   PetscFunctionReturn(PETSC_SUCCESS);
668 }
669 
670 /*@C
671 
672   PCPatchSetComputeOperatorInteriorFacets - Set the callback function used to compute facet integrals for patch matrices
673 
674   Logically Collective
675 
676   Input Parameters:
677 + pc   - The `PC`
678 . func - The callback function
679 - ctx  - The user context
680 
681   Calling sequence of `func`:
682 + pc               - The `PC`
683 . point            - The point
684 . x                - The input solution (not used in linear problems)
685 . mat              - The patch matrix
686 . facetIS          - An array of the facet numbers
687 . n                - The size of `dofsArray`
688 . dofsArray        - The dofmap for the dofs to be solved for
689 . dofsArrayWithAll - The dofmap for all dofs on the patch
690 - ctx              - The user context
691 
692   Level: advanced
693 
694   Note:
695   The matrix entries have been set to zero before the call.
696 
697 .seealso: [](ch_ksp), `PCPatchGetComputeOperator()`, `PCPatchSetComputeFunction()`, `PCPatchSetDiscretisationInfo()`
698 @*/
699 PetscErrorCode PCPatchSetComputeOperatorInteriorFacets(PC pc, PetscErrorCode (*func)(PC pc, PetscInt point, Vec x, Mat mat, IS facetIS, PetscInt n, const PetscInt *dofsArray, const PetscInt *dofsArrayWithAll, void *ctx), void *ctx)
700 {
701   PC_PATCH *patch = (PC_PATCH *)pc->data;
702 
703   PetscFunctionBegin;
704   patch->usercomputeopintfacet    = func;
705   patch->usercomputeopintfacetctx = ctx;
706   PetscFunctionReturn(PETSC_SUCCESS);
707 }
708 
709 /* On entry, ht contains the topological entities whose dofs we are responsible for solving for;
710    on exit, cht contains all the topological entities we need to compute their residuals.
711    In full generality this should incorporate knowledge of the sparsity pattern of the matrix;
712    here we assume a standard FE sparsity pattern.*/
713 /* TODO: Use DMPlexGetAdjacency() */
714 static PetscErrorCode PCPatchCompleteCellPatch(PC pc, PetscHSetI ht, PetscHSetI cht)
715 {
716   DM            dm, plex;
717   PC_PATCH     *patch = (PC_PATCH *)pc->data;
718   PetscHashIter hi;
719   PetscInt      point;
720   PetscInt     *star = NULL, *closure = NULL;
721   PetscInt      ignoredim, iStart = 0, iEnd = -1, starSize, closureSize, si, ci;
722   PetscInt     *fStar = NULL, *fClosure = NULL;
723   PetscInt      fBegin, fEnd, fsi, fci, fStarSize, fClosureSize;
724 
725   PetscFunctionBegin;
726   PetscCall(PCGetDM(pc, &dm));
727   PetscCall(DMConvert(dm, DMPLEX, &plex));
728   dm = plex;
729   PetscCall(DMPlexGetHeightStratum(dm, 1, &fBegin, &fEnd));
730   PetscCall(PCPatchGetIgnoreDim(pc, &ignoredim));
731   if (ignoredim >= 0) PetscCall(DMPlexGetDepthStratum(dm, ignoredim, &iStart, &iEnd));
732   PetscCall(PetscHSetIClear(cht));
733   PetscHashIterBegin(ht, hi);
734   while (!PetscHashIterAtEnd(ht, hi)) {
735     PetscHashIterGetKey(ht, hi, point);
736     PetscHashIterNext(ht, hi);
737 
738     /* Loop over all the cells that this point connects to */
739     PetscCall(DMPlexGetTransitiveClosure(dm, point, PETSC_FALSE, &starSize, &star));
740     for (si = 0; si < starSize * 2; si += 2) {
741       const PetscInt ownedpoint = star[si];
742       /* TODO Check for point in cht before running through closure again */
743       /* now loop over all entities in the closure of that cell */
744       PetscCall(DMPlexGetTransitiveClosure(dm, ownedpoint, PETSC_TRUE, &closureSize, &closure));
745       for (ci = 0; ci < closureSize * 2; ci += 2) {
746         const PetscInt seenpoint = closure[ci];
747         if (ignoredim >= 0 && seenpoint >= iStart && seenpoint < iEnd) continue;
748         PetscCall(PetscHSetIAdd(cht, seenpoint));
749         /* Facet integrals couple dofs across facets, so in that case for each of
750           the facets we need to add all dofs on the other side of the facet to
751           the seen dofs. */
752         if (patch->usercomputeopintfacet) {
753           if (fBegin <= seenpoint && seenpoint < fEnd) {
754             PetscCall(DMPlexGetTransitiveClosure(dm, seenpoint, PETSC_FALSE, &fStarSize, &fStar));
755             for (fsi = 0; fsi < fStarSize * 2; fsi += 2) {
756               PetscCall(DMPlexGetTransitiveClosure(dm, fStar[fsi], PETSC_TRUE, &fClosureSize, &fClosure));
757               for (fci = 0; fci < fClosureSize * 2; fci += 2) PetscCall(PetscHSetIAdd(cht, fClosure[fci]));
758               PetscCall(DMPlexRestoreTransitiveClosure(dm, fStar[fsi], PETSC_TRUE, NULL, &fClosure));
759             }
760             PetscCall(DMPlexRestoreTransitiveClosure(dm, seenpoint, PETSC_FALSE, NULL, &fStar));
761           }
762         }
763       }
764       PetscCall(DMPlexRestoreTransitiveClosure(dm, ownedpoint, PETSC_TRUE, NULL, &closure));
765     }
766     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_FALSE, NULL, &star));
767   }
768   PetscCall(DMDestroy(&dm));
769   PetscFunctionReturn(PETSC_SUCCESS);
770 }
771 
772 static PetscErrorCode PCPatchGetGlobalDofs(PC pc, PetscSection dofSection[], PetscInt f, PetscBool combined, PetscInt p, PetscInt *dof, PetscInt *off)
773 {
774   PetscFunctionBegin;
775   if (combined) {
776     if (f < 0) {
777       if (dof) PetscCall(PetscSectionGetDof(dofSection[0], p, dof));
778       if (off) PetscCall(PetscSectionGetOffset(dofSection[0], p, off));
779     } else {
780       if (dof) PetscCall(PetscSectionGetFieldDof(dofSection[0], p, f, dof));
781       if (off) PetscCall(PetscSectionGetFieldOffset(dofSection[0], p, f, off));
782     }
783   } else {
784     if (f < 0) {
785       PC_PATCH *patch = (PC_PATCH *)pc->data;
786       PetscInt  fdof, g;
787 
788       if (dof) {
789         *dof = 0;
790         for (g = 0; g < patch->nsubspaces; ++g) {
791           PetscCall(PetscSectionGetDof(dofSection[g], p, &fdof));
792           *dof += fdof;
793         }
794       }
795       if (off) {
796         *off = 0;
797         for (g = 0; g < patch->nsubspaces; ++g) {
798           PetscCall(PetscSectionGetOffset(dofSection[g], p, &fdof));
799           *off += fdof;
800         }
801       }
802     } else {
803       if (dof) PetscCall(PetscSectionGetDof(dofSection[f], p, dof));
804       if (off) PetscCall(PetscSectionGetOffset(dofSection[f], p, off));
805     }
806   }
807   PetscFunctionReturn(PETSC_SUCCESS);
808 }
809 
810 /* Given a hash table with a set of topological entities (pts), compute the degrees of
811    freedom in global concatenated numbering on those entities.
812    For Vanka smoothing, this needs to do something special: ignore dofs of the
813    constraint subspace on entities that aren't the base entity we're building the patch
814    around. */
815 static PetscErrorCode PCPatchGetPointDofs(PC pc, PetscHSetI pts, PetscHSetI dofs, PetscInt base, PetscHSetI *subspaces_to_exclude)
816 {
817   PC_PATCH     *patch = (PC_PATCH *)pc->data;
818   PetscHashIter hi;
819   PetscInt      ldof, loff;
820   PetscInt      k, p;
821 
822   PetscFunctionBegin;
823   PetscCall(PetscHSetIClear(dofs));
824   for (k = 0; k < patch->nsubspaces; ++k) {
825     PetscInt subspaceOffset = patch->subspaceOffsets[k];
826     PetscInt bs             = patch->bs[k];
827     PetscInt j, l;
828 
829     if (subspaces_to_exclude != NULL) {
830       PetscBool should_exclude_k = PETSC_FALSE;
831       PetscCall(PetscHSetIHas(*subspaces_to_exclude, k, &should_exclude_k));
832       if (should_exclude_k) {
833         /* only get this subspace dofs at the base entity, not any others */
834         PetscCall(PCPatchGetGlobalDofs(pc, patch->dofSection, k, patch->combined, base, &ldof, &loff));
835         if (0 == ldof) continue;
836         for (j = loff; j < ldof + loff; ++j) {
837           for (l = 0; l < bs; ++l) {
838             PetscInt dof = bs * j + l + subspaceOffset;
839             PetscCall(PetscHSetIAdd(dofs, dof));
840           }
841         }
842         continue; /* skip the other dofs of this subspace */
843       }
844     }
845 
846     PetscHashIterBegin(pts, hi);
847     while (!PetscHashIterAtEnd(pts, hi)) {
848       PetscHashIterGetKey(pts, hi, p);
849       PetscHashIterNext(pts, hi);
850       PetscCall(PCPatchGetGlobalDofs(pc, patch->dofSection, k, patch->combined, p, &ldof, &loff));
851       if (0 == ldof) continue;
852       for (j = loff; j < ldof + loff; ++j) {
853         for (l = 0; l < bs; ++l) {
854           PetscInt dof = bs * j + l + subspaceOffset;
855           PetscCall(PetscHSetIAdd(dofs, dof));
856         }
857       }
858     }
859   }
860   PetscFunctionReturn(PETSC_SUCCESS);
861 }
862 
863 /* Given two hash tables A and B, compute the keys in B that are not in A, and put them in C */
864 static PetscErrorCode PCPatchComputeSetDifference_Private(PetscHSetI A, PetscHSetI B, PetscHSetI C)
865 {
866   PetscHashIter hi;
867   PetscInt      key;
868   PetscBool     flg;
869 
870   PetscFunctionBegin;
871   PetscCall(PetscHSetIClear(C));
872   PetscHashIterBegin(B, hi);
873   while (!PetscHashIterAtEnd(B, hi)) {
874     PetscHashIterGetKey(B, hi, key);
875     PetscHashIterNext(B, hi);
876     PetscCall(PetscHSetIHas(A, key, &flg));
877     if (!flg) PetscCall(PetscHSetIAdd(C, key));
878   }
879   PetscFunctionReturn(PETSC_SUCCESS);
880 }
881 
882 // PetscClangLinter pragma disable: -fdoc-sowing-chars
883 /*
884   PCPatchCreateCellPatches - create patches.
885 
886   Input Parameter:
887   . dm - The DMPlex object defining the mesh
888 
889   Output Parameters:
890   + cellCounts  - Section with counts of cells around each vertex
891   . cells       - IS of the cell point indices of cells in each patch
892   . pointCounts - Section with counts of cells around each vertex
893   - point       - IS of the cell point indices of cells in each patch
894  */
895 static PetscErrorCode PCPatchCreateCellPatches(PC pc)
896 {
897   PC_PATCH       *patch = (PC_PATCH *)pc->data;
898   DMLabel         ghost = NULL;
899   DM              dm, plex;
900   PetscHSetI      ht = NULL, cht = NULL;
901   PetscSection    cellCounts, pointCounts, intFacetCounts, extFacetCounts;
902   PetscInt       *cellsArray, *pointsArray, *intFacetsArray, *extFacetsArray, *intFacetsToPatchCell;
903   PetscInt        numCells, numPoints, numIntFacets, numExtFacets;
904   const PetscInt *leaves;
905   PetscInt        nleaves, pStart, pEnd, cStart, cEnd, vStart, vEnd, fStart, fEnd, v;
906   PetscBool       isFiredrake;
907 
908   PetscFunctionBegin;
909   /* Used to keep track of the cells in the patch. */
910   PetscCall(PetscHSetICreate(&ht));
911   PetscCall(PetscHSetICreate(&cht));
912 
913   PetscCall(PCGetDM(pc, &dm));
914   PetscCheck(dm, PetscObjectComm((PetscObject)pc), PETSC_ERR_ARG_WRONGSTATE, "DM not yet set on patch PC");
915   PetscCall(DMConvert(dm, DMPLEX, &plex));
916   dm = plex;
917   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
918   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
919 
920   if (patch->user_patches) {
921     PetscCall(patch->userpatchconstructionop(pc, &patch->npatch, &patch->userIS, &patch->iterationSet, patch->userpatchconstructctx));
922     vStart = 0;
923     vEnd   = patch->npatch;
924   } else if (patch->ctype == PC_PATCH_PARDECOMP) {
925     vStart = 0;
926     vEnd   = 1;
927   } else if (patch->codim < 0) {
928     if (patch->dim < 0) PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
929     else PetscCall(DMPlexGetDepthStratum(dm, patch->dim, &vStart, &vEnd));
930   } else PetscCall(DMPlexGetHeightStratum(dm, patch->codim, &vStart, &vEnd));
931   patch->npatch = vEnd - vStart;
932 
933   /* These labels mark the owned points.  We only create patches around points that this process owns. */
934   PetscCall(DMHasLabel(dm, "pyop2_ghost", &isFiredrake));
935   if (isFiredrake) {
936     PetscCall(DMGetLabel(dm, "pyop2_ghost", &ghost));
937     PetscCall(DMLabelCreateIndex(ghost, pStart, pEnd));
938   } else {
939     PetscSF sf;
940 
941     PetscCall(DMGetPointSF(dm, &sf));
942     PetscCall(PetscSFGetGraph(sf, NULL, &nleaves, &leaves, NULL));
943     nleaves = PetscMax(nleaves, 0);
944   }
945 
946   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &patch->cellCounts));
947   PetscCall(PetscObjectSetName((PetscObject)patch->cellCounts, "Patch Cell Layout"));
948   cellCounts = patch->cellCounts;
949   PetscCall(PetscSectionSetChart(cellCounts, vStart, vEnd));
950   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &patch->pointCounts));
951   PetscCall(PetscObjectSetName((PetscObject)patch->pointCounts, "Patch Point Layout"));
952   pointCounts = patch->pointCounts;
953   PetscCall(PetscSectionSetChart(pointCounts, vStart, vEnd));
954   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &patch->extFacetCounts));
955   PetscCall(PetscObjectSetName((PetscObject)patch->extFacetCounts, "Patch Exterior Facet Layout"));
956   extFacetCounts = patch->extFacetCounts;
957   PetscCall(PetscSectionSetChart(extFacetCounts, vStart, vEnd));
958   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &patch->intFacetCounts));
959   PetscCall(PetscObjectSetName((PetscObject)patch->intFacetCounts, "Patch Interior Facet Layout"));
960   intFacetCounts = patch->intFacetCounts;
961   PetscCall(PetscSectionSetChart(intFacetCounts, vStart, vEnd));
962   /* Count cells and points in the patch surrounding each entity */
963   PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
964   for (v = vStart; v < vEnd; ++v) {
965     PetscHashIter hi;
966     PetscInt      chtSize, loc = -1;
967     PetscBool     flg;
968 
969     if (!patch->user_patches && patch->ctype != PC_PATCH_PARDECOMP) {
970       if (ghost) PetscCall(DMLabelHasPoint(ghost, v, &flg));
971       else {
972         PetscCall(PetscFindInt(v, nleaves, leaves, &loc));
973         flg = loc >= 0 ? PETSC_TRUE : PETSC_FALSE;
974       }
975       /* Not an owned entity, don't make a cell patch. */
976       if (flg) continue;
977     }
978 
979     PetscCall(patch->patchconstructop((void *)patch, dm, v, ht));
980     PetscCall(PCPatchCompleteCellPatch(pc, ht, cht));
981     PetscCall(PetscHSetIGetSize(cht, &chtSize));
982     /* empty patch, continue */
983     if (chtSize == 0) continue;
984 
985     /* safe because size(cht) > 0 from above */
986     PetscHashIterBegin(cht, hi);
987     while (!PetscHashIterAtEnd(cht, hi)) {
988       PetscInt point, pdof;
989 
990       PetscHashIterGetKey(cht, hi, point);
991       if (fStart <= point && point < fEnd) {
992         const PetscInt *support;
993         PetscInt        supportSize, p;
994         PetscBool       interior = PETSC_TRUE;
995         PetscCall(DMPlexGetSupport(dm, point, &support));
996         PetscCall(DMPlexGetSupportSize(dm, point, &supportSize));
997         if (supportSize == 1) {
998           interior = PETSC_FALSE;
999         } else {
1000           for (p = 0; p < supportSize; p++) {
1001             PetscBool found;
1002             /* FIXME: can I do this while iterating over cht? */
1003             PetscCall(PetscHSetIHas(cht, support[p], &found));
1004             if (!found) {
1005               interior = PETSC_FALSE;
1006               break;
1007             }
1008           }
1009         }
1010         if (interior) {
1011           PetscCall(PetscSectionAddDof(intFacetCounts, v, 1));
1012         } else {
1013           PetscCall(PetscSectionAddDof(extFacetCounts, v, 1));
1014         }
1015       }
1016       PetscCall(PCPatchGetGlobalDofs(pc, patch->dofSection, -1, patch->combined, point, &pdof, NULL));
1017       if (pdof) PetscCall(PetscSectionAddDof(pointCounts, v, 1));
1018       if (point >= cStart && point < cEnd) PetscCall(PetscSectionAddDof(cellCounts, v, 1));
1019       PetscHashIterNext(cht, hi);
1020     }
1021   }
1022   if (isFiredrake) PetscCall(DMLabelDestroyIndex(ghost));
1023 
1024   PetscCall(PetscSectionSetUp(cellCounts));
1025   PetscCall(PetscSectionGetStorageSize(cellCounts, &numCells));
1026   PetscCall(PetscMalloc1(numCells, &cellsArray));
1027   PetscCall(PetscSectionSetUp(pointCounts));
1028   PetscCall(PetscSectionGetStorageSize(pointCounts, &numPoints));
1029   PetscCall(PetscMalloc1(numPoints, &pointsArray));
1030 
1031   PetscCall(PetscSectionSetUp(intFacetCounts));
1032   PetscCall(PetscSectionSetUp(extFacetCounts));
1033   PetscCall(PetscSectionGetStorageSize(intFacetCounts, &numIntFacets));
1034   PetscCall(PetscSectionGetStorageSize(extFacetCounts, &numExtFacets));
1035   PetscCall(PetscMalloc1(numIntFacets, &intFacetsArray));
1036   PetscCall(PetscMalloc1(numIntFacets * 2, &intFacetsToPatchCell));
1037   PetscCall(PetscMalloc1(numExtFacets, &extFacetsArray));
1038 
1039   /* Now that we know how much space we need, run through again and actually remember the cells. */
1040   for (v = vStart; v < vEnd; v++) {
1041     PetscHashIter hi;
1042     PetscInt      dof, off, cdof, coff, efdof, efoff, ifdof, ifoff, pdof, n = 0, cn = 0, ifn = 0, efn = 0;
1043 
1044     PetscCall(PetscSectionGetDof(pointCounts, v, &dof));
1045     PetscCall(PetscSectionGetOffset(pointCounts, v, &off));
1046     PetscCall(PetscSectionGetDof(cellCounts, v, &cdof));
1047     PetscCall(PetscSectionGetOffset(cellCounts, v, &coff));
1048     PetscCall(PetscSectionGetDof(intFacetCounts, v, &ifdof));
1049     PetscCall(PetscSectionGetOffset(intFacetCounts, v, &ifoff));
1050     PetscCall(PetscSectionGetDof(extFacetCounts, v, &efdof));
1051     PetscCall(PetscSectionGetOffset(extFacetCounts, v, &efoff));
1052     if (dof <= 0) continue;
1053     PetscCall(patch->patchconstructop((void *)patch, dm, v, ht));
1054     PetscCall(PCPatchCompleteCellPatch(pc, ht, cht));
1055     PetscHashIterBegin(cht, hi);
1056     while (!PetscHashIterAtEnd(cht, hi)) {
1057       PetscInt point;
1058 
1059       PetscHashIterGetKey(cht, hi, point);
1060       if (fStart <= point && point < fEnd) {
1061         const PetscInt *support;
1062         PetscInt        supportSize, p;
1063         PetscBool       interior = PETSC_TRUE;
1064         PetscCall(DMPlexGetSupport(dm, point, &support));
1065         PetscCall(DMPlexGetSupportSize(dm, point, &supportSize));
1066         if (supportSize == 1) {
1067           interior = PETSC_FALSE;
1068         } else {
1069           for (p = 0; p < supportSize; p++) {
1070             PetscBool found;
1071             /* FIXME: can I do this while iterating over cht? */
1072             PetscCall(PetscHSetIHas(cht, support[p], &found));
1073             if (!found) {
1074               interior = PETSC_FALSE;
1075               break;
1076             }
1077           }
1078         }
1079         if (interior) {
1080           intFacetsToPatchCell[2 * (ifoff + ifn)]     = support[0];
1081           intFacetsToPatchCell[2 * (ifoff + ifn) + 1] = support[1];
1082           intFacetsArray[ifoff + ifn++]               = point;
1083         } else {
1084           extFacetsArray[efoff + efn++] = point;
1085         }
1086       }
1087       PetscCall(PCPatchGetGlobalDofs(pc, patch->dofSection, -1, patch->combined, point, &pdof, NULL));
1088       if (pdof) pointsArray[off + n++] = point;
1089       if (point >= cStart && point < cEnd) cellsArray[coff + cn++] = point;
1090       PetscHashIterNext(cht, hi);
1091     }
1092     PetscCheck(ifn == ifdof, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Number of interior facets in patch %" PetscInt_FMT " is %" PetscInt_FMT ", but should be %" PetscInt_FMT, v, ifn, ifdof);
1093     PetscCheck(efn == efdof, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Number of exterior facets in patch %" PetscInt_FMT " is %" PetscInt_FMT ", but should be %" PetscInt_FMT, v, efn, efdof);
1094     PetscCheck(cn == cdof, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Number of cells in patch %" PetscInt_FMT " is %" PetscInt_FMT ", but should be %" PetscInt_FMT, v, cn, cdof);
1095     PetscCheck(n == dof, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Number of points in patch %" PetscInt_FMT " is %" PetscInt_FMT ", but should be %" PetscInt_FMT, v, n, dof);
1096 
1097     for (ifn = 0; ifn < ifdof; ifn++) {
1098       PetscInt  cell0  = intFacetsToPatchCell[2 * (ifoff + ifn)];
1099       PetscInt  cell1  = intFacetsToPatchCell[2 * (ifoff + ifn) + 1];
1100       PetscBool found0 = PETSC_FALSE, found1 = PETSC_FALSE;
1101       for (n = 0; n < cdof; n++) {
1102         if (!found0 && cell0 == cellsArray[coff + n]) {
1103           intFacetsToPatchCell[2 * (ifoff + ifn)] = n;
1104           found0                                  = PETSC_TRUE;
1105         }
1106         if (!found1 && cell1 == cellsArray[coff + n]) {
1107           intFacetsToPatchCell[2 * (ifoff + ifn) + 1] = n;
1108           found1                                      = PETSC_TRUE;
1109         }
1110         if (found0 && found1) break;
1111       }
1112       PetscCheck(found0 && found1, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Didn't manage to find local point numbers for facet support");
1113     }
1114   }
1115   PetscCall(PetscHSetIDestroy(&ht));
1116   PetscCall(PetscHSetIDestroy(&cht));
1117 
1118   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, numCells, cellsArray, PETSC_OWN_POINTER, &patch->cells));
1119   PetscCall(PetscObjectSetName((PetscObject)patch->cells, "Patch Cells"));
1120   if (patch->viewCells) {
1121     PetscCall(ObjectView((PetscObject)patch->cellCounts, patch->viewerCells, patch->formatCells));
1122     PetscCall(ObjectView((PetscObject)patch->cells, patch->viewerCells, patch->formatCells));
1123   }
1124   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, numIntFacets, intFacetsArray, PETSC_OWN_POINTER, &patch->intFacets));
1125   PetscCall(PetscObjectSetName((PetscObject)patch->intFacets, "Patch Interior Facets"));
1126   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, 2 * numIntFacets, intFacetsToPatchCell, PETSC_OWN_POINTER, &patch->intFacetsToPatchCell));
1127   PetscCall(PetscObjectSetName((PetscObject)patch->intFacetsToPatchCell, "Patch Interior Facets local support"));
1128   if (patch->viewIntFacets) {
1129     PetscCall(ObjectView((PetscObject)patch->intFacetCounts, patch->viewerIntFacets, patch->formatIntFacets));
1130     PetscCall(ObjectView((PetscObject)patch->intFacets, patch->viewerIntFacets, patch->formatIntFacets));
1131     PetscCall(ObjectView((PetscObject)patch->intFacetsToPatchCell, patch->viewerIntFacets, patch->formatIntFacets));
1132   }
1133   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, numExtFacets, extFacetsArray, PETSC_OWN_POINTER, &patch->extFacets));
1134   PetscCall(PetscObjectSetName((PetscObject)patch->extFacets, "Patch Exterior Facets"));
1135   if (patch->viewExtFacets) {
1136     PetscCall(ObjectView((PetscObject)patch->extFacetCounts, patch->viewerExtFacets, patch->formatExtFacets));
1137     PetscCall(ObjectView((PetscObject)patch->extFacets, patch->viewerExtFacets, patch->formatExtFacets));
1138   }
1139   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, numPoints, pointsArray, PETSC_OWN_POINTER, &patch->points));
1140   PetscCall(PetscObjectSetName((PetscObject)patch->points, "Patch Points"));
1141   if (patch->viewPoints) {
1142     PetscCall(ObjectView((PetscObject)patch->pointCounts, patch->viewerPoints, patch->formatPoints));
1143     PetscCall(ObjectView((PetscObject)patch->points, patch->viewerPoints, patch->formatPoints));
1144   }
1145   PetscCall(DMDestroy(&dm));
1146   PetscFunctionReturn(PETSC_SUCCESS);
1147 }
1148 
1149 /*
1150   PCPatchCreateCellPatchDiscretisationInfo - Build the dof maps for cell patches
1151 
1152   Input Parameters:
1153   + dm - The DMPlex object defining the mesh
1154   . cellCounts - Section with counts of cells around each vertex
1155   . cells - IS of the cell point indices of cells in each patch
1156   . cellNumbering - Section mapping plex cell points to Firedrake cell indices.
1157   . nodesPerCell - number of nodes per cell.
1158   - cellNodeMap - map from cells to node indices (nodesPerCell * numCells)
1159 
1160   Output Parameters:
1161   + dofs - IS of local dof numbers of each cell in the patch, where local is a patch local numbering
1162   . gtolCounts - Section with counts of dofs per cell patch
1163   - gtol - IS mapping from global dofs to local dofs for each patch.
1164  */
1165 static PetscErrorCode PCPatchCreateCellPatchDiscretisationInfo(PC pc)
1166 {
1167   PC_PATCH       *patch       = (PC_PATCH *)pc->data;
1168   PetscSection    cellCounts  = patch->cellCounts;
1169   PetscSection    pointCounts = patch->pointCounts;
1170   PetscSection    gtolCounts, gtolCountsWithArtificial = NULL, gtolCountsWithAll = NULL;
1171   IS              cells         = patch->cells;
1172   IS              points        = patch->points;
1173   PetscSection    cellNumbering = patch->cellNumbering;
1174   PetscInt        Nf            = patch->nsubspaces;
1175   PetscInt        numCells, numPoints;
1176   PetscInt        numDofs;
1177   PetscInt        numGlobalDofs, numGlobalDofsWithArtificial, numGlobalDofsWithAll;
1178   PetscInt        totalDofsPerCell = patch->totalDofsPerCell;
1179   PetscInt        vStart, vEnd, v;
1180   const PetscInt *cellsArray, *pointsArray;
1181   PetscInt       *newCellsArray                 = NULL;
1182   PetscInt       *dofsArray                     = NULL;
1183   PetscInt       *dofsArrayWithArtificial       = NULL;
1184   PetscInt       *dofsArrayWithAll              = NULL;
1185   PetscInt       *offsArray                     = NULL;
1186   PetscInt       *offsArrayWithArtificial       = NULL;
1187   PetscInt       *offsArrayWithAll              = NULL;
1188   PetscInt       *asmArray                      = NULL;
1189   PetscInt       *asmArrayWithArtificial        = NULL;
1190   PetscInt       *asmArrayWithAll               = NULL;
1191   PetscInt       *globalDofsArray               = NULL;
1192   PetscInt       *globalDofsArrayWithArtificial = NULL;
1193   PetscInt       *globalDofsArrayWithAll        = NULL;
1194   PetscInt        globalIndex                   = 0;
1195   PetscInt        key                           = 0;
1196   PetscInt        asmKey                        = 0;
1197   DM              dm                            = NULL, plex;
1198   const PetscInt *bcNodes                       = NULL;
1199   PetscHMapI      ht;
1200   PetscHMapI      htWithArtificial;
1201   PetscHMapI      htWithAll;
1202   PetscHSetI      globalBcs;
1203   PetscInt        numBcs;
1204   PetscHSetI      ownedpts, seenpts, owneddofs, seendofs, artificialbcs;
1205   PetscInt        pStart, pEnd, p, i;
1206   char            option[PETSC_MAX_PATH_LEN];
1207   PetscBool       isNonlinear;
1208 
1209   PetscFunctionBegin;
1210 
1211   PetscCall(PCGetDM(pc, &dm));
1212   PetscCall(DMConvert(dm, DMPLEX, &plex));
1213   dm = plex;
1214   /* dofcounts section is cellcounts section * dofPerCell */
1215   PetscCall(PetscSectionGetStorageSize(cellCounts, &numCells));
1216   PetscCall(PetscSectionGetStorageSize(patch->pointCounts, &numPoints));
1217   numDofs = numCells * totalDofsPerCell;
1218   PetscCall(PetscMalloc1(numDofs, &dofsArray));
1219   PetscCall(PetscMalloc1(numPoints * Nf, &offsArray));
1220   PetscCall(PetscMalloc1(numDofs, &asmArray));
1221   PetscCall(PetscMalloc1(numCells, &newCellsArray));
1222   PetscCall(PetscSectionGetChart(cellCounts, &vStart, &vEnd));
1223   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &patch->gtolCounts));
1224   gtolCounts = patch->gtolCounts;
1225   PetscCall(PetscSectionSetChart(gtolCounts, vStart, vEnd));
1226   PetscCall(PetscObjectSetName((PetscObject)patch->gtolCounts, "Patch Global Index Section"));
1227 
1228   if (patch->local_composition_type == PC_COMPOSITE_MULTIPLICATIVE) {
1229     PetscCall(PetscMalloc1(numPoints * Nf, &offsArrayWithArtificial));
1230     PetscCall(PetscMalloc1(numDofs, &asmArrayWithArtificial));
1231     PetscCall(PetscMalloc1(numDofs, &dofsArrayWithArtificial));
1232     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &patch->gtolCountsWithArtificial));
1233     gtolCountsWithArtificial = patch->gtolCountsWithArtificial;
1234     PetscCall(PetscSectionSetChart(gtolCountsWithArtificial, vStart, vEnd));
1235     PetscCall(PetscObjectSetName((PetscObject)patch->gtolCountsWithArtificial, "Patch Global Index Section Including Artificial BCs"));
1236   }
1237 
1238   isNonlinear = patch->isNonlinear;
1239   if (isNonlinear) {
1240     PetscCall(PetscMalloc1(numPoints * Nf, &offsArrayWithAll));
1241     PetscCall(PetscMalloc1(numDofs, &asmArrayWithAll));
1242     PetscCall(PetscMalloc1(numDofs, &dofsArrayWithAll));
1243     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &patch->gtolCountsWithAll));
1244     gtolCountsWithAll = patch->gtolCountsWithAll;
1245     PetscCall(PetscSectionSetChart(gtolCountsWithAll, vStart, vEnd));
1246     PetscCall(PetscObjectSetName((PetscObject)patch->gtolCountsWithAll, "Patch Global Index Section Including All BCs"));
1247   }
1248 
1249   /* Outside the patch loop, get the dofs that are globally-enforced Dirichlet
1250    conditions */
1251   PetscCall(PetscHSetICreate(&globalBcs));
1252   PetscCall(ISGetIndices(patch->ghostBcNodes, &bcNodes));
1253   PetscCall(ISGetSize(patch->ghostBcNodes, &numBcs));
1254   for (i = 0; i < numBcs; ++i) { PetscCall(PetscHSetIAdd(globalBcs, bcNodes[i])); /* these are already in concatenated numbering */ }
1255   PetscCall(ISRestoreIndices(patch->ghostBcNodes, &bcNodes));
1256   PetscCall(ISDestroy(&patch->ghostBcNodes)); /* memory optimisation */
1257 
1258   /* Hash tables for artificial BC construction */
1259   PetscCall(PetscHSetICreate(&ownedpts));
1260   PetscCall(PetscHSetICreate(&seenpts));
1261   PetscCall(PetscHSetICreate(&owneddofs));
1262   PetscCall(PetscHSetICreate(&seendofs));
1263   PetscCall(PetscHSetICreate(&artificialbcs));
1264 
1265   PetscCall(ISGetIndices(cells, &cellsArray));
1266   PetscCall(ISGetIndices(points, &pointsArray));
1267   PetscCall(PetscHMapICreate(&ht));
1268   PetscCall(PetscHMapICreate(&htWithArtificial));
1269   PetscCall(PetscHMapICreate(&htWithAll));
1270   for (v = vStart; v < vEnd; ++v) {
1271     PetscInt localIndex               = 0;
1272     PetscInt localIndexWithArtificial = 0;
1273     PetscInt localIndexWithAll        = 0;
1274     PetscInt dof, off, i, j, k, l;
1275 
1276     PetscCall(PetscHMapIClear(ht));
1277     PetscCall(PetscHMapIClear(htWithArtificial));
1278     PetscCall(PetscHMapIClear(htWithAll));
1279     PetscCall(PetscSectionGetDof(cellCounts, v, &dof));
1280     PetscCall(PetscSectionGetOffset(cellCounts, v, &off));
1281     if (dof <= 0) continue;
1282 
1283     /* Calculate the global numbers of the artificial BC dofs here first */
1284     PetscCall(patch->patchconstructop((void *)patch, dm, v, ownedpts));
1285     PetscCall(PCPatchCompleteCellPatch(pc, ownedpts, seenpts));
1286     PetscCall(PCPatchGetPointDofs(pc, ownedpts, owneddofs, v, &patch->subspaces_to_exclude));
1287     PetscCall(PCPatchGetPointDofs(pc, seenpts, seendofs, v, NULL));
1288     PetscCall(PCPatchComputeSetDifference_Private(owneddofs, seendofs, artificialbcs));
1289     if (patch->viewPatches) {
1290       PetscHSetI    globalbcdofs;
1291       PetscHashIter hi;
1292       MPI_Comm      comm = PetscObjectComm((PetscObject)pc);
1293 
1294       PetscCall(PetscHSetICreate(&globalbcdofs));
1295       PetscCall(PetscSynchronizedPrintf(comm, "Patch %" PetscInt_FMT ": owned dofs:\n", v));
1296       PetscHashIterBegin(owneddofs, hi);
1297       while (!PetscHashIterAtEnd(owneddofs, hi)) {
1298         PetscInt globalDof;
1299 
1300         PetscHashIterGetKey(owneddofs, hi, globalDof);
1301         PetscHashIterNext(owneddofs, hi);
1302         PetscCall(PetscSynchronizedPrintf(comm, "%" PetscInt_FMT " ", globalDof));
1303       }
1304       PetscCall(PetscSynchronizedPrintf(comm, "\n"));
1305       PetscCall(PetscSynchronizedPrintf(comm, "Patch %" PetscInt_FMT ": seen dofs:\n", v));
1306       PetscHashIterBegin(seendofs, hi);
1307       while (!PetscHashIterAtEnd(seendofs, hi)) {
1308         PetscInt  globalDof;
1309         PetscBool flg;
1310 
1311         PetscHashIterGetKey(seendofs, hi, globalDof);
1312         PetscHashIterNext(seendofs, hi);
1313         PetscCall(PetscSynchronizedPrintf(comm, "%" PetscInt_FMT " ", globalDof));
1314 
1315         PetscCall(PetscHSetIHas(globalBcs, globalDof, &flg));
1316         if (flg) PetscCall(PetscHSetIAdd(globalbcdofs, globalDof));
1317       }
1318       PetscCall(PetscSynchronizedPrintf(comm, "\n"));
1319       PetscCall(PetscSynchronizedPrintf(comm, "Patch %" PetscInt_FMT ": global BCs:\n", v));
1320       PetscCall(PetscHSetIGetSize(globalbcdofs, &numBcs));
1321       if (numBcs > 0) {
1322         PetscHashIterBegin(globalbcdofs, hi);
1323         while (!PetscHashIterAtEnd(globalbcdofs, hi)) {
1324           PetscInt globalDof;
1325           PetscHashIterGetKey(globalbcdofs, hi, globalDof);
1326           PetscHashIterNext(globalbcdofs, hi);
1327           PetscCall(PetscSynchronizedPrintf(comm, "%" PetscInt_FMT " ", globalDof));
1328         }
1329       }
1330       PetscCall(PetscSynchronizedPrintf(comm, "\n"));
1331       PetscCall(PetscSynchronizedPrintf(comm, "Patch %" PetscInt_FMT ": artificial BCs:\n", v));
1332       PetscCall(PetscHSetIGetSize(artificialbcs, &numBcs));
1333       if (numBcs > 0) {
1334         PetscHashIterBegin(artificialbcs, hi);
1335         while (!PetscHashIterAtEnd(artificialbcs, hi)) {
1336           PetscInt globalDof;
1337           PetscHashIterGetKey(artificialbcs, hi, globalDof);
1338           PetscHashIterNext(artificialbcs, hi);
1339           PetscCall(PetscSynchronizedPrintf(comm, "%" PetscInt_FMT " ", globalDof));
1340         }
1341       }
1342       PetscCall(PetscSynchronizedPrintf(comm, "\n\n"));
1343       PetscCall(PetscHSetIDestroy(&globalbcdofs));
1344     }
1345     for (k = 0; k < patch->nsubspaces; ++k) {
1346       const PetscInt *cellNodeMap    = patch->cellNodeMap[k];
1347       PetscInt        nodesPerCell   = patch->nodesPerCell[k];
1348       PetscInt        subspaceOffset = patch->subspaceOffsets[k];
1349       PetscInt        bs             = patch->bs[k];
1350 
1351       for (i = off; i < off + dof; ++i) {
1352         /* Walk over the cells in this patch. */
1353         const PetscInt c    = cellsArray[i];
1354         PetscInt       cell = c;
1355 
1356         /* TODO Change this to an IS */
1357         if (cellNumbering) {
1358           PetscCall(PetscSectionGetDof(cellNumbering, c, &cell));
1359           PetscCheck(cell > 0, PetscObjectComm((PetscObject)pc), PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " doesn't appear in cell numbering map", c);
1360           PetscCall(PetscSectionGetOffset(cellNumbering, c, &cell));
1361         }
1362         newCellsArray[i] = cell;
1363         for (j = 0; j < nodesPerCell; ++j) {
1364           /* For each global dof, map it into contiguous local storage. */
1365           const PetscInt globalDof = cellNodeMap[cell * nodesPerCell + j] * bs + subspaceOffset;
1366           /* finally, loop over block size */
1367           for (l = 0; l < bs; ++l) {
1368             PetscInt  localDof;
1369             PetscBool isGlobalBcDof, isArtificialBcDof;
1370 
1371             /* first, check if this is either a globally enforced or locally enforced BC dof */
1372             PetscCall(PetscHSetIHas(globalBcs, globalDof + l, &isGlobalBcDof));
1373             PetscCall(PetscHSetIHas(artificialbcs, globalDof + l, &isArtificialBcDof));
1374 
1375             /* if it's either, don't ever give it a local dof number */
1376             if (isGlobalBcDof || isArtificialBcDof) {
1377               dofsArray[globalIndex] = -1; /* don't use this in assembly in this patch */
1378             } else {
1379               PetscCall(PetscHMapIGet(ht, globalDof + l, &localDof));
1380               if (localDof == -1) {
1381                 localDof = localIndex++;
1382                 PetscCall(PetscHMapISet(ht, globalDof + l, localDof));
1383               }
1384               PetscCheck(globalIndex < numDofs, PETSC_COMM_WORLD, PETSC_ERR_ARG_OUTOFRANGE, "Found more dofs %" PetscInt_FMT " than expected %" PetscInt_FMT, globalIndex + 1, numDofs);
1385               /* And store. */
1386               dofsArray[globalIndex] = localDof;
1387             }
1388 
1389             if (patch->local_composition_type == PC_COMPOSITE_MULTIPLICATIVE) {
1390               if (isGlobalBcDof) {
1391                 dofsArrayWithArtificial[globalIndex] = -1; /* don't use this in assembly in this patch */
1392               } else {
1393                 PetscCall(PetscHMapIGet(htWithArtificial, globalDof + l, &localDof));
1394                 if (localDof == -1) {
1395                   localDof = localIndexWithArtificial++;
1396                   PetscCall(PetscHMapISet(htWithArtificial, globalDof + l, localDof));
1397                 }
1398                 PetscCheck(globalIndex < numDofs, PETSC_COMM_WORLD, PETSC_ERR_ARG_OUTOFRANGE, "Found more dofs %" PetscInt_FMT " than expected %" PetscInt_FMT, globalIndex + 1, numDofs);
1399                 /* And store.*/
1400                 dofsArrayWithArtificial[globalIndex] = localDof;
1401               }
1402             }
1403 
1404             if (isNonlinear) {
1405               /* Build the dofmap for the function space with _all_ dofs,
1406    including those in any kind of boundary condition */
1407               PetscCall(PetscHMapIGet(htWithAll, globalDof + l, &localDof));
1408               if (localDof == -1) {
1409                 localDof = localIndexWithAll++;
1410                 PetscCall(PetscHMapISet(htWithAll, globalDof + l, localDof));
1411               }
1412               PetscCheck(globalIndex < numDofs, PETSC_COMM_WORLD, PETSC_ERR_ARG_OUTOFRANGE, "Found more dofs %" PetscInt_FMT " than expected %" PetscInt_FMT, globalIndex + 1, numDofs);
1413               /* And store.*/
1414               dofsArrayWithAll[globalIndex] = localDof;
1415             }
1416             globalIndex++;
1417           }
1418         }
1419       }
1420     }
1421     /*How many local dofs in this patch? */
1422     if (patch->local_composition_type == PC_COMPOSITE_MULTIPLICATIVE) {
1423       PetscCall(PetscHMapIGetSize(htWithArtificial, &dof));
1424       PetscCall(PetscSectionSetDof(gtolCountsWithArtificial, v, dof));
1425     }
1426     if (isNonlinear) {
1427       PetscCall(PetscHMapIGetSize(htWithAll, &dof));
1428       PetscCall(PetscSectionSetDof(gtolCountsWithAll, v, dof));
1429     }
1430     PetscCall(PetscHMapIGetSize(ht, &dof));
1431     PetscCall(PetscSectionSetDof(gtolCounts, v, dof));
1432   }
1433 
1434   PetscCall(DMDestroy(&dm));
1435   PetscCheck(globalIndex == numDofs, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Expected number of dofs (%" PetscInt_FMT ") doesn't match found number (%" PetscInt_FMT ")", numDofs, globalIndex);
1436   PetscCall(PetscSectionSetUp(gtolCounts));
1437   PetscCall(PetscSectionGetStorageSize(gtolCounts, &numGlobalDofs));
1438   PetscCall(PetscMalloc1(numGlobalDofs, &globalDofsArray));
1439 
1440   if (patch->local_composition_type == PC_COMPOSITE_MULTIPLICATIVE) {
1441     PetscCall(PetscSectionSetUp(gtolCountsWithArtificial));
1442     PetscCall(PetscSectionGetStorageSize(gtolCountsWithArtificial, &numGlobalDofsWithArtificial));
1443     PetscCall(PetscMalloc1(numGlobalDofsWithArtificial, &globalDofsArrayWithArtificial));
1444   }
1445   if (isNonlinear) {
1446     PetscCall(PetscSectionSetUp(gtolCountsWithAll));
1447     PetscCall(PetscSectionGetStorageSize(gtolCountsWithAll, &numGlobalDofsWithAll));
1448     PetscCall(PetscMalloc1(numGlobalDofsWithAll, &globalDofsArrayWithAll));
1449   }
1450   /* Now populate the global to local map.  This could be merged into the above loop if we were willing to deal with reallocs. */
1451   for (v = vStart; v < vEnd; ++v) {
1452     PetscHashIter hi;
1453     PetscInt      dof, off, Np, ooff, i, j, k, l;
1454 
1455     PetscCall(PetscHMapIClear(ht));
1456     PetscCall(PetscHMapIClear(htWithArtificial));
1457     PetscCall(PetscHMapIClear(htWithAll));
1458     PetscCall(PetscSectionGetDof(cellCounts, v, &dof));
1459     PetscCall(PetscSectionGetOffset(cellCounts, v, &off));
1460     PetscCall(PetscSectionGetDof(pointCounts, v, &Np));
1461     PetscCall(PetscSectionGetOffset(pointCounts, v, &ooff));
1462     if (dof <= 0) continue;
1463 
1464     for (k = 0; k < patch->nsubspaces; ++k) {
1465       const PetscInt *cellNodeMap    = patch->cellNodeMap[k];
1466       PetscInt        nodesPerCell   = patch->nodesPerCell[k];
1467       PetscInt        subspaceOffset = patch->subspaceOffsets[k];
1468       PetscInt        bs             = patch->bs[k];
1469       PetscInt        goff;
1470 
1471       for (i = off; i < off + dof; ++i) {
1472         /* Reconstruct mapping of global-to-local on this patch. */
1473         const PetscInt c    = cellsArray[i];
1474         PetscInt       cell = c;
1475 
1476         if (cellNumbering) PetscCall(PetscSectionGetOffset(cellNumbering, c, &cell));
1477         for (j = 0; j < nodesPerCell; ++j) {
1478           for (l = 0; l < bs; ++l) {
1479             const PetscInt globalDof = cellNodeMap[cell * nodesPerCell + j] * bs + l + subspaceOffset;
1480             const PetscInt localDof  = dofsArray[key];
1481             if (localDof >= 0) PetscCall(PetscHMapISet(ht, globalDof, localDof));
1482             if (patch->local_composition_type == PC_COMPOSITE_MULTIPLICATIVE) {
1483               const PetscInt localDofWithArtificial = dofsArrayWithArtificial[key];
1484               if (localDofWithArtificial >= 0) PetscCall(PetscHMapISet(htWithArtificial, globalDof, localDofWithArtificial));
1485             }
1486             if (isNonlinear) {
1487               const PetscInt localDofWithAll = dofsArrayWithAll[key];
1488               if (localDofWithAll >= 0) PetscCall(PetscHMapISet(htWithAll, globalDof, localDofWithAll));
1489             }
1490             key++;
1491           }
1492         }
1493       }
1494 
1495       /* Shove it in the output data structure. */
1496       PetscCall(PetscSectionGetOffset(gtolCounts, v, &goff));
1497       PetscHashIterBegin(ht, hi);
1498       while (!PetscHashIterAtEnd(ht, hi)) {
1499         PetscInt globalDof, localDof;
1500 
1501         PetscHashIterGetKey(ht, hi, globalDof);
1502         PetscHashIterGetVal(ht, hi, localDof);
1503         if (globalDof >= 0) globalDofsArray[goff + localDof] = globalDof;
1504         PetscHashIterNext(ht, hi);
1505       }
1506 
1507       if (patch->local_composition_type == PC_COMPOSITE_MULTIPLICATIVE) {
1508         PetscCall(PetscSectionGetOffset(gtolCountsWithArtificial, v, &goff));
1509         PetscHashIterBegin(htWithArtificial, hi);
1510         while (!PetscHashIterAtEnd(htWithArtificial, hi)) {
1511           PetscInt globalDof, localDof;
1512           PetscHashIterGetKey(htWithArtificial, hi, globalDof);
1513           PetscHashIterGetVal(htWithArtificial, hi, localDof);
1514           if (globalDof >= 0) globalDofsArrayWithArtificial[goff + localDof] = globalDof;
1515           PetscHashIterNext(htWithArtificial, hi);
1516         }
1517       }
1518       if (isNonlinear) {
1519         PetscCall(PetscSectionGetOffset(gtolCountsWithAll, v, &goff));
1520         PetscHashIterBegin(htWithAll, hi);
1521         while (!PetscHashIterAtEnd(htWithAll, hi)) {
1522           PetscInt globalDof, localDof;
1523           PetscHashIterGetKey(htWithAll, hi, globalDof);
1524           PetscHashIterGetVal(htWithAll, hi, localDof);
1525           if (globalDof >= 0) globalDofsArrayWithAll[goff + localDof] = globalDof;
1526           PetscHashIterNext(htWithAll, hi);
1527         }
1528       }
1529 
1530       for (p = 0; p < Np; ++p) {
1531         const PetscInt point = pointsArray[ooff + p];
1532         PetscInt       globalDof, localDof;
1533 
1534         PetscCall(PCPatchGetGlobalDofs(pc, patch->dofSection, k, patch->combined, point, NULL, &globalDof));
1535         PetscCall(PetscHMapIGet(ht, globalDof, &localDof));
1536         offsArray[(ooff + p) * Nf + k] = localDof;
1537         if (patch->local_composition_type == PC_COMPOSITE_MULTIPLICATIVE) {
1538           PetscCall(PetscHMapIGet(htWithArtificial, globalDof, &localDof));
1539           offsArrayWithArtificial[(ooff + p) * Nf + k] = localDof;
1540         }
1541         if (isNonlinear) {
1542           PetscCall(PetscHMapIGet(htWithAll, globalDof, &localDof));
1543           offsArrayWithAll[(ooff + p) * Nf + k] = localDof;
1544         }
1545       }
1546     }
1547 
1548     PetscCall(PetscHSetIDestroy(&globalBcs));
1549     PetscCall(PetscHSetIDestroy(&ownedpts));
1550     PetscCall(PetscHSetIDestroy(&seenpts));
1551     PetscCall(PetscHSetIDestroy(&owneddofs));
1552     PetscCall(PetscHSetIDestroy(&seendofs));
1553     PetscCall(PetscHSetIDestroy(&artificialbcs));
1554 
1555     /* At this point, we have a hash table ht built that maps globalDof -> localDof.
1556    We need to create the dof table laid out cellwise first, then by subspace,
1557    as the assembler assembles cell-wise and we need to stuff the different
1558    contributions of the different function spaces to the right places. So we loop
1559    over cells, then over subspaces. */
1560     if (patch->nsubspaces > 1) { /* for nsubspaces = 1, data we need is already in dofsArray */
1561       for (i = off; i < off + dof; ++i) {
1562         const PetscInt c    = cellsArray[i];
1563         PetscInt       cell = c;
1564 
1565         if (cellNumbering) PetscCall(PetscSectionGetOffset(cellNumbering, c, &cell));
1566         for (k = 0; k < patch->nsubspaces; ++k) {
1567           const PetscInt *cellNodeMap    = patch->cellNodeMap[k];
1568           PetscInt        nodesPerCell   = patch->nodesPerCell[k];
1569           PetscInt        subspaceOffset = patch->subspaceOffsets[k];
1570           PetscInt        bs             = patch->bs[k];
1571 
1572           for (j = 0; j < nodesPerCell; ++j) {
1573             for (l = 0; l < bs; ++l) {
1574               const PetscInt globalDof = cellNodeMap[cell * nodesPerCell + j] * bs + l + subspaceOffset;
1575               PetscInt       localDof;
1576 
1577               PetscCall(PetscHMapIGet(ht, globalDof, &localDof));
1578               /* If it's not in the hash table, i.e. is a BC dof,
1579    then the PetscHSetIMap above gives -1, which matches
1580    exactly the convention for PETSc's matrix assembly to
1581    ignore the dof. So we don't need to do anything here */
1582               asmArray[asmKey] = localDof;
1583               if (patch->local_composition_type == PC_COMPOSITE_MULTIPLICATIVE) {
1584                 PetscCall(PetscHMapIGet(htWithArtificial, globalDof, &localDof));
1585                 asmArrayWithArtificial[asmKey] = localDof;
1586               }
1587               if (isNonlinear) {
1588                 PetscCall(PetscHMapIGet(htWithAll, globalDof, &localDof));
1589                 asmArrayWithAll[asmKey] = localDof;
1590               }
1591               asmKey++;
1592             }
1593           }
1594         }
1595       }
1596     }
1597   }
1598   if (1 == patch->nsubspaces) {
1599     PetscCall(PetscArraycpy(asmArray, dofsArray, numDofs));
1600     if (patch->local_composition_type == PC_COMPOSITE_MULTIPLICATIVE) PetscCall(PetscArraycpy(asmArrayWithArtificial, dofsArrayWithArtificial, numDofs));
1601     if (isNonlinear) PetscCall(PetscArraycpy(asmArrayWithAll, dofsArrayWithAll, numDofs));
1602   }
1603 
1604   PetscCall(PetscHMapIDestroy(&ht));
1605   PetscCall(PetscHMapIDestroy(&htWithArtificial));
1606   PetscCall(PetscHMapIDestroy(&htWithAll));
1607   PetscCall(ISRestoreIndices(cells, &cellsArray));
1608   PetscCall(ISRestoreIndices(points, &pointsArray));
1609   PetscCall(PetscFree(dofsArray));
1610   if (patch->local_composition_type == PC_COMPOSITE_MULTIPLICATIVE) PetscCall(PetscFree(dofsArrayWithArtificial));
1611   if (isNonlinear) PetscCall(PetscFree(dofsArrayWithAll));
1612   /* Create placeholder section for map from points to patch dofs */
1613   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &patch->patchSection));
1614   PetscCall(PetscSectionSetNumFields(patch->patchSection, patch->nsubspaces));
1615   if (patch->combined) {
1616     PetscInt numFields;
1617     PetscCall(PetscSectionGetNumFields(patch->dofSection[0], &numFields));
1618     PetscCheck(numFields == patch->nsubspaces, PetscObjectComm((PetscObject)pc), PETSC_ERR_ARG_WRONG, "Mismatch between number of section fields %" PetscInt_FMT " and number of subspaces %" PetscInt_FMT, numFields, patch->nsubspaces);
1619     PetscCall(PetscSectionGetChart(patch->dofSection[0], &pStart, &pEnd));
1620     PetscCall(PetscSectionSetChart(patch->patchSection, pStart, pEnd));
1621     for (p = pStart; p < pEnd; ++p) {
1622       PetscInt dof, fdof, f;
1623 
1624       PetscCall(PetscSectionGetDof(patch->dofSection[0], p, &dof));
1625       PetscCall(PetscSectionSetDof(patch->patchSection, p, dof));
1626       for (f = 0; f < patch->nsubspaces; ++f) {
1627         PetscCall(PetscSectionGetFieldDof(patch->dofSection[0], p, f, &fdof));
1628         PetscCall(PetscSectionSetFieldDof(patch->patchSection, p, f, fdof));
1629       }
1630     }
1631   } else {
1632     PetscInt pStartf, pEndf, f;
1633     pStart = PETSC_MAX_INT;
1634     pEnd   = PETSC_MIN_INT;
1635     for (f = 0; f < patch->nsubspaces; ++f) {
1636       PetscCall(PetscSectionGetChart(patch->dofSection[f], &pStartf, &pEndf));
1637       pStart = PetscMin(pStart, pStartf);
1638       pEnd   = PetscMax(pEnd, pEndf);
1639     }
1640     PetscCall(PetscSectionSetChart(patch->patchSection, pStart, pEnd));
1641     for (f = 0; f < patch->nsubspaces; ++f) {
1642       PetscCall(PetscSectionGetChart(patch->dofSection[f], &pStartf, &pEndf));
1643       for (p = pStartf; p < pEndf; ++p) {
1644         PetscInt fdof;
1645         PetscCall(PetscSectionGetDof(patch->dofSection[f], p, &fdof));
1646         PetscCall(PetscSectionAddDof(patch->patchSection, p, fdof));
1647         PetscCall(PetscSectionSetFieldDof(patch->patchSection, p, f, fdof));
1648       }
1649     }
1650   }
1651   PetscCall(PetscSectionSetUp(patch->patchSection));
1652   PetscCall(PetscSectionSetUseFieldOffsets(patch->patchSection, PETSC_TRUE));
1653   /* Replace cell indices with firedrake-numbered ones. */
1654   PetscCall(ISGeneralSetIndices(cells, numCells, (const PetscInt *)newCellsArray, PETSC_OWN_POINTER));
1655   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, numGlobalDofs, globalDofsArray, PETSC_OWN_POINTER, &patch->gtol));
1656   PetscCall(PetscObjectSetName((PetscObject)patch->gtol, "Global Indices"));
1657   PetscCall(PetscSNPrintf(option, PETSC_MAX_PATH_LEN, "-%s_patch_g2l_view", patch->classname));
1658   PetscCall(PetscSectionViewFromOptions(patch->gtolCounts, (PetscObject)pc, option));
1659   PetscCall(ISViewFromOptions(patch->gtol, (PetscObject)pc, option));
1660   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, numDofs, asmArray, PETSC_OWN_POINTER, &patch->dofs));
1661   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, numPoints * Nf, offsArray, PETSC_OWN_POINTER, &patch->offs));
1662   if (patch->local_composition_type == PC_COMPOSITE_MULTIPLICATIVE) {
1663     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, numGlobalDofsWithArtificial, globalDofsArrayWithArtificial, PETSC_OWN_POINTER, &patch->gtolWithArtificial));
1664     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, numDofs, asmArrayWithArtificial, PETSC_OWN_POINTER, &patch->dofsWithArtificial));
1665     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, numPoints * Nf, offsArrayWithArtificial, PETSC_OWN_POINTER, &patch->offsWithArtificial));
1666   }
1667   if (isNonlinear) {
1668     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, numGlobalDofsWithAll, globalDofsArrayWithAll, PETSC_OWN_POINTER, &patch->gtolWithAll));
1669     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, numDofs, asmArrayWithAll, PETSC_OWN_POINTER, &patch->dofsWithAll));
1670     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, numPoints * Nf, offsArrayWithAll, PETSC_OWN_POINTER, &patch->offsWithAll));
1671   }
1672   PetscFunctionReturn(PETSC_SUCCESS);
1673 }
1674 
1675 static PetscErrorCode PCPatchCreateMatrix_Private(PC pc, PetscInt point, Mat *mat, PetscBool withArtificial)
1676 {
1677   PC_PATCH   *patch = (PC_PATCH *)pc->data;
1678   PetscBool   flg;
1679   PetscInt    csize, rsize;
1680   const char *prefix = NULL;
1681 
1682   PetscFunctionBegin;
1683   if (withArtificial) {
1684     /* would be nice if we could create a rectangular matrix of size numDofsWithArtificial x numDofs here */
1685     PetscInt pStart;
1686     PetscCall(PetscSectionGetChart(patch->gtolCountsWithArtificial, &pStart, NULL));
1687     PetscCall(PetscSectionGetDof(patch->gtolCountsWithArtificial, point + pStart, &rsize));
1688     csize = rsize;
1689   } else {
1690     PetscInt pStart;
1691     PetscCall(PetscSectionGetChart(patch->gtolCounts, &pStart, NULL));
1692     PetscCall(PetscSectionGetDof(patch->gtolCounts, point + pStart, &rsize));
1693     csize = rsize;
1694   }
1695 
1696   PetscCall(MatCreate(PETSC_COMM_SELF, mat));
1697   PetscCall(PCGetOptionsPrefix(pc, &prefix));
1698   PetscCall(MatSetOptionsPrefix(*mat, prefix));
1699   PetscCall(MatAppendOptionsPrefix(*mat, "pc_patch_sub_"));
1700   if (patch->sub_mat_type) PetscCall(MatSetType(*mat, patch->sub_mat_type));
1701   else if (!patch->sub_mat_type) PetscCall(MatSetType(*mat, MATDENSE));
1702   PetscCall(MatSetSizes(*mat, rsize, csize, rsize, csize));
1703   PetscCall(PetscObjectTypeCompare((PetscObject)*mat, MATDENSE, &flg));
1704   if (!flg) PetscCall(PetscObjectTypeCompare((PetscObject)*mat, MATSEQDENSE, &flg));
1705   /* Sparse patch matrices */
1706   if (!flg) {
1707     PetscBT         bt;
1708     PetscInt       *dnnz      = NULL;
1709     const PetscInt *dofsArray = NULL;
1710     PetscInt        pStart, pEnd, ncell, offset, c, i, j;
1711 
1712     if (withArtificial) {
1713       PetscCall(ISGetIndices(patch->dofsWithArtificial, &dofsArray));
1714     } else {
1715       PetscCall(ISGetIndices(patch->dofs, &dofsArray));
1716     }
1717     PetscCall(PetscSectionGetChart(patch->cellCounts, &pStart, &pEnd));
1718     point += pStart;
1719     PetscCheck(point < pEnd, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Operator point %" PetscInt_FMT " not in [%" PetscInt_FMT ", %" PetscInt_FMT ")", point, pStart, pEnd);
1720     PetscCall(PetscSectionGetDof(patch->cellCounts, point, &ncell));
1721     PetscCall(PetscSectionGetOffset(patch->cellCounts, point, &offset));
1722     PetscCall(PetscLogEventBegin(PC_Patch_Prealloc, pc, 0, 0, 0));
1723     /* A PetscBT uses N^2 bits to store the sparsity pattern on a
1724    * patch. This is probably OK if the patches are not too big,
1725    * but uses too much memory. We therefore switch based on rsize. */
1726     if (rsize < 3000) { /* FIXME: I picked this switch value out of my hat */
1727       PetscScalar *zeroes;
1728       PetscInt     rows;
1729 
1730       PetscCall(PetscCalloc1(rsize, &dnnz));
1731       PetscCall(PetscBTCreate(rsize * rsize, &bt));
1732       for (c = 0; c < ncell; ++c) {
1733         const PetscInt *idx = dofsArray + (offset + c) * patch->totalDofsPerCell;
1734         for (i = 0; i < patch->totalDofsPerCell; ++i) {
1735           const PetscInt row = idx[i];
1736           if (row < 0) continue;
1737           for (j = 0; j < patch->totalDofsPerCell; ++j) {
1738             const PetscInt col = idx[j];
1739             const PetscInt key = row * rsize + col;
1740             if (col < 0) continue;
1741             if (!PetscBTLookupSet(bt, key)) ++dnnz[row];
1742           }
1743         }
1744       }
1745 
1746       if (patch->usercomputeopintfacet) {
1747         const PetscInt *intFacetsArray = NULL;
1748         PetscInt        i, numIntFacets, intFacetOffset;
1749         const PetscInt *facetCells = NULL;
1750 
1751         PetscCall(PetscSectionGetDof(patch->intFacetCounts, point, &numIntFacets));
1752         PetscCall(PetscSectionGetOffset(patch->intFacetCounts, point, &intFacetOffset));
1753         PetscCall(ISGetIndices(patch->intFacetsToPatchCell, &facetCells));
1754         PetscCall(ISGetIndices(patch->intFacets, &intFacetsArray));
1755         for (i = 0; i < numIntFacets; i++) {
1756           const PetscInt cell0 = facetCells[2 * (intFacetOffset + i) + 0];
1757           const PetscInt cell1 = facetCells[2 * (intFacetOffset + i) + 1];
1758           PetscInt       celli, cellj;
1759 
1760           for (celli = 0; celli < patch->totalDofsPerCell; celli++) {
1761             const PetscInt row = dofsArray[(offset + cell0) * patch->totalDofsPerCell + celli];
1762             if (row < 0) continue;
1763             for (cellj = 0; cellj < patch->totalDofsPerCell; cellj++) {
1764               const PetscInt col = dofsArray[(offset + cell1) * patch->totalDofsPerCell + cellj];
1765               const PetscInt key = row * rsize + col;
1766               if (col < 0) continue;
1767               if (!PetscBTLookupSet(bt, key)) ++dnnz[row];
1768             }
1769           }
1770 
1771           for (celli = 0; celli < patch->totalDofsPerCell; celli++) {
1772             const PetscInt row = dofsArray[(offset + cell1) * patch->totalDofsPerCell + celli];
1773             if (row < 0) continue;
1774             for (cellj = 0; cellj < patch->totalDofsPerCell; cellj++) {
1775               const PetscInt col = dofsArray[(offset + cell0) * patch->totalDofsPerCell + cellj];
1776               const PetscInt key = row * rsize + col;
1777               if (col < 0) continue;
1778               if (!PetscBTLookupSet(bt, key)) ++dnnz[row];
1779             }
1780           }
1781         }
1782       }
1783       PetscCall(PetscBTDestroy(&bt));
1784       PetscCall(MatXAIJSetPreallocation(*mat, 1, dnnz, NULL, NULL, NULL));
1785       PetscCall(PetscFree(dnnz));
1786 
1787       PetscCall(PetscCalloc1(patch->totalDofsPerCell * patch->totalDofsPerCell, &zeroes));
1788       for (c = 0; c < ncell; ++c) {
1789         const PetscInt *idx = &dofsArray[(offset + c) * patch->totalDofsPerCell];
1790         PetscCall(MatSetValues(*mat, patch->totalDofsPerCell, idx, patch->totalDofsPerCell, idx, zeroes, INSERT_VALUES));
1791       }
1792       PetscCall(MatGetLocalSize(*mat, &rows, NULL));
1793       for (i = 0; i < rows; ++i) PetscCall(MatSetValues(*mat, 1, &i, 1, &i, zeroes, INSERT_VALUES));
1794 
1795       if (patch->usercomputeopintfacet) {
1796         const PetscInt *intFacetsArray = NULL;
1797         PetscInt        i, numIntFacets, intFacetOffset;
1798         const PetscInt *facetCells = NULL;
1799 
1800         PetscCall(PetscSectionGetDof(patch->intFacetCounts, point, &numIntFacets));
1801         PetscCall(PetscSectionGetOffset(patch->intFacetCounts, point, &intFacetOffset));
1802         PetscCall(ISGetIndices(patch->intFacetsToPatchCell, &facetCells));
1803         PetscCall(ISGetIndices(patch->intFacets, &intFacetsArray));
1804         for (i = 0; i < numIntFacets; i++) {
1805           const PetscInt  cell0    = facetCells[2 * (intFacetOffset + i) + 0];
1806           const PetscInt  cell1    = facetCells[2 * (intFacetOffset + i) + 1];
1807           const PetscInt *cell0idx = &dofsArray[(offset + cell0) * patch->totalDofsPerCell];
1808           const PetscInt *cell1idx = &dofsArray[(offset + cell1) * patch->totalDofsPerCell];
1809           PetscCall(MatSetValues(*mat, patch->totalDofsPerCell, cell0idx, patch->totalDofsPerCell, cell1idx, zeroes, INSERT_VALUES));
1810           PetscCall(MatSetValues(*mat, patch->totalDofsPerCell, cell1idx, patch->totalDofsPerCell, cell0idx, zeroes, INSERT_VALUES));
1811         }
1812       }
1813 
1814       PetscCall(MatAssemblyBegin(*mat, MAT_FINAL_ASSEMBLY));
1815       PetscCall(MatAssemblyEnd(*mat, MAT_FINAL_ASSEMBLY));
1816 
1817       PetscCall(PetscFree(zeroes));
1818 
1819     } else { /* rsize too big, use MATPREALLOCATOR */
1820       Mat          preallocator;
1821       PetscScalar *vals;
1822 
1823       PetscCall(PetscCalloc1(patch->totalDofsPerCell * patch->totalDofsPerCell, &vals));
1824       PetscCall(MatCreate(PETSC_COMM_SELF, &preallocator));
1825       PetscCall(MatSetType(preallocator, MATPREALLOCATOR));
1826       PetscCall(MatSetSizes(preallocator, rsize, rsize, rsize, rsize));
1827       PetscCall(MatSetUp(preallocator));
1828 
1829       for (c = 0; c < ncell; ++c) {
1830         const PetscInt *idx = dofsArray + (offset + c) * patch->totalDofsPerCell;
1831         PetscCall(MatSetValues(preallocator, patch->totalDofsPerCell, idx, patch->totalDofsPerCell, idx, vals, INSERT_VALUES));
1832       }
1833 
1834       if (patch->usercomputeopintfacet) {
1835         const PetscInt *intFacetsArray = NULL;
1836         PetscInt        i, numIntFacets, intFacetOffset;
1837         const PetscInt *facetCells = NULL;
1838 
1839         PetscCall(PetscSectionGetDof(patch->intFacetCounts, point, &numIntFacets));
1840         PetscCall(PetscSectionGetOffset(patch->intFacetCounts, point, &intFacetOffset));
1841         PetscCall(ISGetIndices(patch->intFacetsToPatchCell, &facetCells));
1842         PetscCall(ISGetIndices(patch->intFacets, &intFacetsArray));
1843         for (i = 0; i < numIntFacets; i++) {
1844           const PetscInt  cell0    = facetCells[2 * (intFacetOffset + i) + 0];
1845           const PetscInt  cell1    = facetCells[2 * (intFacetOffset + i) + 1];
1846           const PetscInt *cell0idx = &dofsArray[(offset + cell0) * patch->totalDofsPerCell];
1847           const PetscInt *cell1idx = &dofsArray[(offset + cell1) * patch->totalDofsPerCell];
1848           PetscCall(MatSetValues(preallocator, patch->totalDofsPerCell, cell0idx, patch->totalDofsPerCell, cell1idx, vals, INSERT_VALUES));
1849           PetscCall(MatSetValues(preallocator, patch->totalDofsPerCell, cell1idx, patch->totalDofsPerCell, cell0idx, vals, INSERT_VALUES));
1850         }
1851       }
1852 
1853       PetscCall(PetscFree(vals));
1854       PetscCall(MatAssemblyBegin(preallocator, MAT_FINAL_ASSEMBLY));
1855       PetscCall(MatAssemblyEnd(preallocator, MAT_FINAL_ASSEMBLY));
1856       PetscCall(MatPreallocatorPreallocate(preallocator, PETSC_TRUE, *mat));
1857       PetscCall(MatDestroy(&preallocator));
1858     }
1859     PetscCall(PetscLogEventEnd(PC_Patch_Prealloc, pc, 0, 0, 0));
1860     if (withArtificial) {
1861       PetscCall(ISRestoreIndices(patch->dofsWithArtificial, &dofsArray));
1862     } else {
1863       PetscCall(ISRestoreIndices(patch->dofs, &dofsArray));
1864     }
1865   }
1866   PetscCall(MatSetUp(*mat));
1867   PetscFunctionReturn(PETSC_SUCCESS);
1868 }
1869 
1870 static PetscErrorCode PCPatchComputeFunction_DMPlex_Private(PC pc, PetscInt patchNum, Vec x, Vec F, IS cellIS, PetscInt n, const PetscInt *l2p, const PetscInt *l2pWithAll, void *ctx)
1871 {
1872   PC_PATCH       *patch = (PC_PATCH *)pc->data;
1873   DM              dm, plex;
1874   PetscSection    s;
1875   const PetscInt *parray, *oarray;
1876   PetscInt        Nf = patch->nsubspaces, Np, poff, p, f;
1877 
1878   PetscFunctionBegin;
1879   PetscCheck(!patch->precomputeElementTensors, PetscObjectComm((PetscObject)pc), PETSC_ERR_ARG_WRONGSTATE, "Precomputing element tensors not implemented with DMPlex compute operator");
1880   PetscCall(PCGetDM(pc, &dm));
1881   PetscCall(DMConvert(dm, DMPLEX, &plex));
1882   dm = plex;
1883   PetscCall(DMGetLocalSection(dm, &s));
1884   /* Set offset into patch */
1885   PetscCall(PetscSectionGetDof(patch->pointCounts, patchNum, &Np));
1886   PetscCall(PetscSectionGetOffset(patch->pointCounts, patchNum, &poff));
1887   PetscCall(ISGetIndices(patch->points, &parray));
1888   PetscCall(ISGetIndices(patch->offs, &oarray));
1889   for (f = 0; f < Nf; ++f) {
1890     for (p = 0; p < Np; ++p) {
1891       const PetscInt point = parray[poff + p];
1892       PetscInt       dof;
1893 
1894       PetscCall(PetscSectionGetFieldDof(patch->patchSection, point, f, &dof));
1895       PetscCall(PetscSectionSetFieldOffset(patch->patchSection, point, f, oarray[(poff + p) * Nf + f]));
1896       if (patch->nsubspaces == 1) PetscCall(PetscSectionSetOffset(patch->patchSection, point, oarray[(poff + p) * Nf + f]));
1897       else PetscCall(PetscSectionSetOffset(patch->patchSection, point, -1));
1898     }
1899   }
1900   PetscCall(ISRestoreIndices(patch->points, &parray));
1901   PetscCall(ISRestoreIndices(patch->offs, &oarray));
1902   if (patch->viewSection) PetscCall(ObjectView((PetscObject)patch->patchSection, patch->viewerSection, patch->formatSection));
1903   PetscCall(DMPlexComputeResidual_Patch_Internal(dm, patch->patchSection, cellIS, 0.0, x, NULL, F, ctx));
1904   PetscCall(DMDestroy(&dm));
1905   PetscFunctionReturn(PETSC_SUCCESS);
1906 }
1907 
1908 PetscErrorCode PCPatchComputeFunction_Internal(PC pc, Vec x, Vec F, PetscInt point)
1909 {
1910   PC_PATCH       *patch = (PC_PATCH *)pc->data;
1911   const PetscInt *dofsArray;
1912   const PetscInt *dofsArrayWithAll;
1913   const PetscInt *cellsArray;
1914   PetscInt        ncell, offset, pStart, pEnd;
1915 
1916   PetscFunctionBegin;
1917   PetscCall(PetscLogEventBegin(PC_Patch_ComputeOp, pc, 0, 0, 0));
1918   PetscCheck(patch->usercomputeop, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "Must call PCPatchSetComputeOperator() to set callback");
1919   PetscCall(ISGetIndices(patch->dofs, &dofsArray));
1920   PetscCall(ISGetIndices(patch->dofsWithAll, &dofsArrayWithAll));
1921   PetscCall(ISGetIndices(patch->cells, &cellsArray));
1922   PetscCall(PetscSectionGetChart(patch->cellCounts, &pStart, &pEnd));
1923 
1924   point += pStart;
1925   PetscCheck(point < pEnd, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Operator point %" PetscInt_FMT " not in [%" PetscInt_FMT ", %" PetscInt_FMT ")", point, pStart, pEnd);
1926 
1927   PetscCall(PetscSectionGetDof(patch->cellCounts, point, &ncell));
1928   PetscCall(PetscSectionGetOffset(patch->cellCounts, point, &offset));
1929   if (ncell <= 0) {
1930     PetscCall(PetscLogEventEnd(PC_Patch_ComputeOp, pc, 0, 0, 0));
1931     PetscFunctionReturn(PETSC_SUCCESS);
1932   }
1933   PetscCall(VecSet(F, 0.0));
1934   /* Cannot reuse the same IS because the geometry info is being cached in it */
1935   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, ncell, cellsArray + offset, PETSC_USE_POINTER, &patch->cellIS));
1936   PetscCallBack("PCPatch callback", patch->usercomputef(pc, point, x, F, patch->cellIS, ncell * patch->totalDofsPerCell, dofsArray + offset * patch->totalDofsPerCell, dofsArrayWithAll + offset * patch->totalDofsPerCell, patch->usercomputefctx));
1937   PetscCall(ISDestroy(&patch->cellIS));
1938   PetscCall(ISRestoreIndices(patch->dofs, &dofsArray));
1939   PetscCall(ISRestoreIndices(patch->dofsWithAll, &dofsArrayWithAll));
1940   PetscCall(ISRestoreIndices(patch->cells, &cellsArray));
1941   if (patch->viewMatrix) {
1942     char name[PETSC_MAX_PATH_LEN];
1943 
1944     PetscCall(PetscSNPrintf(name, PETSC_MAX_PATH_LEN - 1, "Patch vector for Point %" PetscInt_FMT, point));
1945     PetscCall(PetscObjectSetName((PetscObject)F, name));
1946     PetscCall(ObjectView((PetscObject)F, patch->viewerMatrix, patch->formatMatrix));
1947   }
1948   PetscCall(PetscLogEventEnd(PC_Patch_ComputeOp, pc, 0, 0, 0));
1949   PetscFunctionReturn(PETSC_SUCCESS);
1950 }
1951 
1952 static PetscErrorCode PCPatchComputeOperator_DMPlex_Private(PC pc, PetscInt patchNum, Vec x, Mat J, IS cellIS, PetscInt n, const PetscInt *l2p, const PetscInt *l2pWithAll, void *ctx)
1953 {
1954   PC_PATCH       *patch = (PC_PATCH *)pc->data;
1955   DM              dm, plex;
1956   PetscSection    s;
1957   const PetscInt *parray, *oarray;
1958   PetscInt        Nf = patch->nsubspaces, Np, poff, p, f;
1959 
1960   PetscFunctionBegin;
1961   PetscCall(PCGetDM(pc, &dm));
1962   PetscCall(DMConvert(dm, DMPLEX, &plex));
1963   dm = plex;
1964   PetscCall(DMGetLocalSection(dm, &s));
1965   /* Set offset into patch */
1966   PetscCall(PetscSectionGetDof(patch->pointCounts, patchNum, &Np));
1967   PetscCall(PetscSectionGetOffset(patch->pointCounts, patchNum, &poff));
1968   PetscCall(ISGetIndices(patch->points, &parray));
1969   PetscCall(ISGetIndices(patch->offs, &oarray));
1970   for (f = 0; f < Nf; ++f) {
1971     for (p = 0; p < Np; ++p) {
1972       const PetscInt point = parray[poff + p];
1973       PetscInt       dof;
1974 
1975       PetscCall(PetscSectionGetFieldDof(patch->patchSection, point, f, &dof));
1976       PetscCall(PetscSectionSetFieldOffset(patch->patchSection, point, f, oarray[(poff + p) * Nf + f]));
1977       if (patch->nsubspaces == 1) PetscCall(PetscSectionSetOffset(patch->patchSection, point, oarray[(poff + p) * Nf + f]));
1978       else PetscCall(PetscSectionSetOffset(patch->patchSection, point, -1));
1979     }
1980   }
1981   PetscCall(ISRestoreIndices(patch->points, &parray));
1982   PetscCall(ISRestoreIndices(patch->offs, &oarray));
1983   if (patch->viewSection) PetscCall(ObjectView((PetscObject)patch->patchSection, patch->viewerSection, patch->formatSection));
1984   /* TODO Shut off MatViewFromOptions() in MatAssemblyEnd() here */
1985   PetscCall(DMPlexComputeJacobian_Patch_Internal(dm, patch->patchSection, patch->patchSection, cellIS, 0.0, 0.0, x, NULL, J, J, ctx));
1986   PetscCall(DMDestroy(&dm));
1987   PetscFunctionReturn(PETSC_SUCCESS);
1988 }
1989 
1990 /* This function zeros mat on entry */
1991 PetscErrorCode PCPatchComputeOperator_Internal(PC pc, Vec x, Mat mat, PetscInt point, PetscBool withArtificial)
1992 {
1993   PC_PATCH       *patch = (PC_PATCH *)pc->data;
1994   const PetscInt *dofsArray;
1995   const PetscInt *dofsArrayWithAll = NULL;
1996   const PetscInt *cellsArray;
1997   PetscInt        ncell, offset, pStart, pEnd, numIntFacets, intFacetOffset;
1998   PetscBool       isNonlinear;
1999 
2000   PetscFunctionBegin;
2001   PetscCall(PetscLogEventBegin(PC_Patch_ComputeOp, pc, 0, 0, 0));
2002   isNonlinear = patch->isNonlinear;
2003   PetscCheck(patch->usercomputeop, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "Must call PCPatchSetComputeOperator() to set callback");
2004   if (withArtificial) {
2005     PetscCall(ISGetIndices(patch->dofsWithArtificial, &dofsArray));
2006   } else {
2007     PetscCall(ISGetIndices(patch->dofs, &dofsArray));
2008   }
2009   if (isNonlinear) PetscCall(ISGetIndices(patch->dofsWithAll, &dofsArrayWithAll));
2010   PetscCall(ISGetIndices(patch->cells, &cellsArray));
2011   PetscCall(PetscSectionGetChart(patch->cellCounts, &pStart, &pEnd));
2012 
2013   point += pStart;
2014   PetscCheck(point < pEnd, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Operator point %" PetscInt_FMT " not in [%" PetscInt_FMT ", %" PetscInt_FMT ")", point, pStart, pEnd);
2015 
2016   PetscCall(PetscSectionGetDof(patch->cellCounts, point, &ncell));
2017   PetscCall(PetscSectionGetOffset(patch->cellCounts, point, &offset));
2018   if (ncell <= 0) {
2019     PetscCall(PetscLogEventEnd(PC_Patch_ComputeOp, pc, 0, 0, 0));
2020     PetscFunctionReturn(PETSC_SUCCESS);
2021   }
2022   PetscCall(MatZeroEntries(mat));
2023   if (patch->precomputeElementTensors) {
2024     PetscInt           i;
2025     PetscInt           ndof = patch->totalDofsPerCell;
2026     const PetscScalar *elementTensors;
2027 
2028     PetscCall(VecGetArrayRead(patch->cellMats, &elementTensors));
2029     for (i = 0; i < ncell; i++) {
2030       const PetscInt     cell = cellsArray[i + offset];
2031       const PetscInt    *idx  = dofsArray + (offset + i) * ndof;
2032       const PetscScalar *v    = elementTensors + patch->precomputedTensorLocations[cell] * ndof * ndof;
2033       PetscCall(MatSetValues(mat, ndof, idx, ndof, idx, v, ADD_VALUES));
2034     }
2035     PetscCall(VecRestoreArrayRead(patch->cellMats, &elementTensors));
2036     PetscCall(MatAssemblyBegin(mat, MAT_FINAL_ASSEMBLY));
2037     PetscCall(MatAssemblyEnd(mat, MAT_FINAL_ASSEMBLY));
2038   } else {
2039     /* Cannot reuse the same IS because the geometry info is being cached in it */
2040     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, ncell, cellsArray + offset, PETSC_USE_POINTER, &patch->cellIS));
2041     PetscCallBack("PCPatch callback",
2042                   patch->usercomputeop(pc, point, x, mat, patch->cellIS, ncell * patch->totalDofsPerCell, dofsArray + offset * patch->totalDofsPerCell, dofsArrayWithAll ? dofsArrayWithAll + offset * patch->totalDofsPerCell : NULL, patch->usercomputeopctx));
2043   }
2044   if (patch->usercomputeopintfacet) {
2045     PetscCall(PetscSectionGetDof(patch->intFacetCounts, point, &numIntFacets));
2046     PetscCall(PetscSectionGetOffset(patch->intFacetCounts, point, &intFacetOffset));
2047     if (numIntFacets > 0) {
2048       /* For each interior facet, grab the two cells (in local numbering, and concatenate dof numberings for those cells) */
2049       PetscInt       *facetDofs = NULL, *facetDofsWithAll = NULL;
2050       const PetscInt *intFacetsArray = NULL;
2051       PetscInt        idx            = 0;
2052       PetscInt        i, c, d;
2053       PetscInt        fStart;
2054       DM              dm, plex;
2055       IS              facetIS    = NULL;
2056       const PetscInt *facetCells = NULL;
2057 
2058       PetscCall(ISGetIndices(patch->intFacetsToPatchCell, &facetCells));
2059       PetscCall(ISGetIndices(patch->intFacets, &intFacetsArray));
2060       PetscCall(PCGetDM(pc, &dm));
2061       PetscCall(DMConvert(dm, DMPLEX, &plex));
2062       dm = plex;
2063       PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, NULL));
2064       /* FIXME: Pull this malloc out. */
2065       PetscCall(PetscMalloc1(2 * patch->totalDofsPerCell * numIntFacets, &facetDofs));
2066       if (dofsArrayWithAll) PetscCall(PetscMalloc1(2 * patch->totalDofsPerCell * numIntFacets, &facetDofsWithAll));
2067       if (patch->precomputeElementTensors) {
2068         PetscInt           nFacetDof = 2 * patch->totalDofsPerCell;
2069         const PetscScalar *elementTensors;
2070 
2071         PetscCall(VecGetArrayRead(patch->intFacetMats, &elementTensors));
2072 
2073         for (i = 0; i < numIntFacets; i++) {
2074           const PetscInt     facet = intFacetsArray[i + intFacetOffset];
2075           const PetscScalar *v     = elementTensors + patch->precomputedIntFacetTensorLocations[facet - fStart] * nFacetDof * nFacetDof;
2076           idx                      = 0;
2077           /*
2078      0--1
2079      |\-|
2080      |+\|
2081      2--3
2082      [0, 2, 3, 0, 1, 3]
2083    */
2084           for (c = 0; c < 2; c++) {
2085             const PetscInt cell = facetCells[2 * (intFacetOffset + i) + c];
2086             for (d = 0; d < patch->totalDofsPerCell; d++) {
2087               facetDofs[idx] = dofsArray[(offset + cell) * patch->totalDofsPerCell + d];
2088               idx++;
2089             }
2090           }
2091           PetscCall(MatSetValues(mat, nFacetDof, facetDofs, nFacetDof, facetDofs, v, ADD_VALUES));
2092         }
2093         PetscCall(VecRestoreArrayRead(patch->intFacetMats, &elementTensors));
2094       } else {
2095         /*
2096      0--1
2097      |\-|
2098      |+\|
2099      2--3
2100      [0, 2, 3, 0, 1, 3]
2101    */
2102         for (i = 0; i < numIntFacets; i++) {
2103           for (c = 0; c < 2; c++) {
2104             const PetscInt cell = facetCells[2 * (intFacetOffset + i) + c];
2105             for (d = 0; d < patch->totalDofsPerCell; d++) {
2106               facetDofs[idx] = dofsArray[(offset + cell) * patch->totalDofsPerCell + d];
2107               if (dofsArrayWithAll) facetDofsWithAll[idx] = dofsArrayWithAll[(offset + cell) * patch->totalDofsPerCell + d];
2108               idx++;
2109             }
2110           }
2111         }
2112         PetscCall(ISCreateGeneral(PETSC_COMM_SELF, numIntFacets, intFacetsArray + intFacetOffset, PETSC_USE_POINTER, &facetIS));
2113         PetscCall(patch->usercomputeopintfacet(pc, point, x, mat, facetIS, 2 * numIntFacets * patch->totalDofsPerCell, facetDofs, facetDofsWithAll, patch->usercomputeopintfacetctx));
2114         PetscCall(ISDestroy(&facetIS));
2115       }
2116       PetscCall(ISRestoreIndices(patch->intFacetsToPatchCell, &facetCells));
2117       PetscCall(ISRestoreIndices(patch->intFacets, &intFacetsArray));
2118       PetscCall(PetscFree(facetDofs));
2119       PetscCall(PetscFree(facetDofsWithAll));
2120       PetscCall(DMDestroy(&dm));
2121     }
2122   }
2123 
2124   PetscCall(MatAssemblyBegin(mat, MAT_FINAL_ASSEMBLY));
2125   PetscCall(MatAssemblyEnd(mat, MAT_FINAL_ASSEMBLY));
2126 
2127   if (!(withArtificial || isNonlinear) && patch->denseinverse) {
2128     MatFactorInfo info;
2129     PetscBool     flg;
2130     PetscCall(PetscObjectTypeCompare((PetscObject)mat, MATSEQDENSE, &flg));
2131     PetscCheck(flg, PetscObjectComm((PetscObject)pc), PETSC_ERR_ARG_WRONGSTATE, "Invalid Mat type for dense inverse");
2132     PetscCall(MatFactorInfoInitialize(&info));
2133     PetscCall(MatLUFactor(mat, NULL, NULL, &info));
2134     PetscCall(MatSeqDenseInvertFactors_Private(mat));
2135   }
2136   PetscCall(ISDestroy(&patch->cellIS));
2137   if (withArtificial) {
2138     PetscCall(ISRestoreIndices(patch->dofsWithArtificial, &dofsArray));
2139   } else {
2140     PetscCall(ISRestoreIndices(patch->dofs, &dofsArray));
2141   }
2142   if (isNonlinear) PetscCall(ISRestoreIndices(patch->dofsWithAll, &dofsArrayWithAll));
2143   PetscCall(ISRestoreIndices(patch->cells, &cellsArray));
2144   if (patch->viewMatrix) {
2145     char name[PETSC_MAX_PATH_LEN];
2146 
2147     PetscCall(PetscSNPrintf(name, PETSC_MAX_PATH_LEN - 1, "Patch matrix for Point %" PetscInt_FMT, point));
2148     PetscCall(PetscObjectSetName((PetscObject)mat, name));
2149     PetscCall(ObjectView((PetscObject)mat, patch->viewerMatrix, patch->formatMatrix));
2150   }
2151   PetscCall(PetscLogEventEnd(PC_Patch_ComputeOp, pc, 0, 0, 0));
2152   PetscFunctionReturn(PETSC_SUCCESS);
2153 }
2154 
2155 static PetscErrorCode MatSetValues_PCPatch_Private(Mat mat, PetscInt m, const PetscInt idxm[], PetscInt n, const PetscInt idxn[], const PetscScalar *v, InsertMode addv)
2156 {
2157   Vec          data;
2158   PetscScalar *array;
2159   PetscInt     bs, nz, i, j, cell;
2160 
2161   PetscCall(MatShellGetContext(mat, &data));
2162   PetscCall(VecGetBlockSize(data, &bs));
2163   PetscCall(VecGetSize(data, &nz));
2164   PetscCall(VecGetArray(data, &array));
2165   PetscCheck(m == n, PetscObjectComm((PetscObject)mat), PETSC_ERR_ARG_WRONG, "Only for square insertion");
2166   cell = (PetscInt)(idxm[0] / bs); /* use the fact that this is called once per cell */
2167   for (i = 0; i < m; i++) {
2168     PetscCheck(idxm[i] == idxn[i], PetscObjectComm((PetscObject)mat), PETSC_ERR_ARG_WRONG, "Row and column indices must match!");
2169     for (j = 0; j < n; j++) {
2170       const PetscScalar v_ = v[i * bs + j];
2171       /* Indexing is special to the data structure we have! */
2172       if (addv == INSERT_VALUES) {
2173         array[cell * bs * bs + i * bs + j] = v_;
2174       } else {
2175         array[cell * bs * bs + i * bs + j] += v_;
2176       }
2177     }
2178   }
2179   PetscCall(VecRestoreArray(data, &array));
2180   PetscFunctionReturn(PETSC_SUCCESS);
2181 }
2182 
2183 static PetscErrorCode PCPatchPrecomputePatchTensors_Private(PC pc)
2184 {
2185   PC_PATCH       *patch = (PC_PATCH *)pc->data;
2186   const PetscInt *cellsArray;
2187   PetscInt        ncell, offset;
2188   const PetscInt *dofMapArray;
2189   PetscInt        i, j;
2190   IS              dofMap;
2191   IS              cellIS;
2192   const PetscInt  ndof = patch->totalDofsPerCell;
2193   Mat             vecMat;
2194   PetscInt        cStart, cEnd;
2195   DM              dm, plex;
2196 
2197   PetscCall(ISGetSize(patch->cells, &ncell));
2198   if (!ncell) { /* No cells to assemble over -> skip */
2199     PetscFunctionReturn(PETSC_SUCCESS);
2200   }
2201 
2202   PetscCall(PetscLogEventBegin(PC_Patch_ComputeOp, pc, 0, 0, 0));
2203 
2204   PetscCall(PCGetDM(pc, &dm));
2205   PetscCall(DMConvert(dm, DMPLEX, &plex));
2206   dm = plex;
2207   if (!patch->allCells) {
2208     PetscHSetI    cells;
2209     PetscHashIter hi;
2210     PetscInt      pStart, pEnd;
2211     PetscInt     *allCells = NULL;
2212     PetscCall(PetscHSetICreate(&cells));
2213     PetscCall(ISGetIndices(patch->cells, &cellsArray));
2214     PetscCall(PetscSectionGetChart(patch->cellCounts, &pStart, &pEnd));
2215     for (i = pStart; i < pEnd; i++) {
2216       PetscCall(PetscSectionGetDof(patch->cellCounts, i, &ncell));
2217       PetscCall(PetscSectionGetOffset(patch->cellCounts, i, &offset));
2218       if (ncell <= 0) continue;
2219       for (j = 0; j < ncell; j++) PetscCall(PetscHSetIAdd(cells, cellsArray[offset + j]));
2220     }
2221     PetscCall(ISRestoreIndices(patch->cells, &cellsArray));
2222     PetscCall(PetscHSetIGetSize(cells, &ncell));
2223     PetscCall(PetscMalloc1(ncell, &allCells));
2224     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
2225     PetscCall(PetscMalloc1(cEnd - cStart, &patch->precomputedTensorLocations));
2226     i = 0;
2227     PetscHashIterBegin(cells, hi);
2228     while (!PetscHashIterAtEnd(cells, hi)) {
2229       PetscHashIterGetKey(cells, hi, allCells[i]);
2230       patch->precomputedTensorLocations[allCells[i]] = i;
2231       PetscHashIterNext(cells, hi);
2232       i++;
2233     }
2234     PetscCall(PetscHSetIDestroy(&cells));
2235     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, ncell, allCells, PETSC_OWN_POINTER, &patch->allCells));
2236   }
2237   PetscCall(ISGetSize(patch->allCells, &ncell));
2238   if (!patch->cellMats) {
2239     PetscCall(VecCreateSeq(PETSC_COMM_SELF, ncell * ndof * ndof, &patch->cellMats));
2240     PetscCall(VecSetBlockSize(patch->cellMats, ndof));
2241   }
2242   PetscCall(VecSet(patch->cellMats, 0));
2243 
2244   PetscCall(MatCreateShell(PETSC_COMM_SELF, ncell * ndof, ncell * ndof, ncell * ndof, ncell * ndof, (void *)patch->cellMats, &vecMat));
2245   PetscCall(MatShellSetOperation(vecMat, MATOP_SET_VALUES, (void (*)(void)) & MatSetValues_PCPatch_Private));
2246   PetscCall(ISGetSize(patch->allCells, &ncell));
2247   PetscCall(ISCreateStride(PETSC_COMM_SELF, ndof * ncell, 0, 1, &dofMap));
2248   PetscCall(ISGetIndices(dofMap, &dofMapArray));
2249   PetscCall(ISGetIndices(patch->allCells, &cellsArray));
2250   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, ncell, cellsArray, PETSC_USE_POINTER, &cellIS));
2251   /* TODO: Fix for DMPlex compute op, this bypasses a lot of the machinery and just assembles every element tensor. */
2252   PetscCallBack("PCPatch callback", patch->usercomputeop(pc, -1, NULL, vecMat, cellIS, ndof * ncell, dofMapArray, NULL, patch->usercomputeopctx));
2253   PetscCall(ISDestroy(&cellIS));
2254   PetscCall(MatDestroy(&vecMat));
2255   PetscCall(ISRestoreIndices(patch->allCells, &cellsArray));
2256   PetscCall(ISRestoreIndices(dofMap, &dofMapArray));
2257   PetscCall(ISDestroy(&dofMap));
2258 
2259   if (patch->usercomputeopintfacet) {
2260     PetscInt        nIntFacets;
2261     IS              intFacetsIS;
2262     const PetscInt *intFacetsArray = NULL;
2263     if (!patch->allIntFacets) {
2264       PetscHSetI    facets;
2265       PetscHashIter hi;
2266       PetscInt      pStart, pEnd, fStart, fEnd;
2267       PetscInt     *allIntFacets = NULL;
2268       PetscCall(PetscHSetICreate(&facets));
2269       PetscCall(ISGetIndices(patch->intFacets, &intFacetsArray));
2270       PetscCall(PetscSectionGetChart(patch->intFacetCounts, &pStart, &pEnd));
2271       PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
2272       for (i = pStart; i < pEnd; i++) {
2273         PetscCall(PetscSectionGetDof(patch->intFacetCounts, i, &nIntFacets));
2274         PetscCall(PetscSectionGetOffset(patch->intFacetCounts, i, &offset));
2275         if (nIntFacets <= 0) continue;
2276         for (j = 0; j < nIntFacets; j++) PetscCall(PetscHSetIAdd(facets, intFacetsArray[offset + j]));
2277       }
2278       PetscCall(ISRestoreIndices(patch->intFacets, &intFacetsArray));
2279       PetscCall(PetscHSetIGetSize(facets, &nIntFacets));
2280       PetscCall(PetscMalloc1(nIntFacets, &allIntFacets));
2281       PetscCall(PetscMalloc1(fEnd - fStart, &patch->precomputedIntFacetTensorLocations));
2282       i = 0;
2283       PetscHashIterBegin(facets, hi);
2284       while (!PetscHashIterAtEnd(facets, hi)) {
2285         PetscHashIterGetKey(facets, hi, allIntFacets[i]);
2286         patch->precomputedIntFacetTensorLocations[allIntFacets[i] - fStart] = i;
2287         PetscHashIterNext(facets, hi);
2288         i++;
2289       }
2290       PetscCall(PetscHSetIDestroy(&facets));
2291       PetscCall(ISCreateGeneral(PETSC_COMM_SELF, nIntFacets, allIntFacets, PETSC_OWN_POINTER, &patch->allIntFacets));
2292     }
2293     PetscCall(ISGetSize(patch->allIntFacets, &nIntFacets));
2294     if (!patch->intFacetMats) {
2295       PetscCall(VecCreateSeq(PETSC_COMM_SELF, nIntFacets * ndof * ndof * 4, &patch->intFacetMats));
2296       PetscCall(VecSetBlockSize(patch->intFacetMats, ndof * 2));
2297     }
2298     PetscCall(VecSet(patch->intFacetMats, 0));
2299 
2300     PetscCall(MatCreateShell(PETSC_COMM_SELF, nIntFacets * ndof * 2, nIntFacets * ndof * 2, nIntFacets * ndof * 2, nIntFacets * ndof * 2, (void *)patch->intFacetMats, &vecMat));
2301     PetscCall(MatShellSetOperation(vecMat, MATOP_SET_VALUES, (void (*)(void)) & MatSetValues_PCPatch_Private));
2302     PetscCall(ISCreateStride(PETSC_COMM_SELF, 2 * ndof * nIntFacets, 0, 1, &dofMap));
2303     PetscCall(ISGetIndices(dofMap, &dofMapArray));
2304     PetscCall(ISGetIndices(patch->allIntFacets, &intFacetsArray));
2305     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, nIntFacets, intFacetsArray, PETSC_USE_POINTER, &intFacetsIS));
2306     /* TODO: Fix for DMPlex compute op, this bypasses a lot of the machinery and just assembles every element tensor. */
2307     PetscCallBack("PCPatch callback (interior facets)", patch->usercomputeopintfacet(pc, -1, NULL, vecMat, intFacetsIS, 2 * ndof * nIntFacets, dofMapArray, NULL, patch->usercomputeopintfacetctx));
2308     PetscCall(ISDestroy(&intFacetsIS));
2309     PetscCall(MatDestroy(&vecMat));
2310     PetscCall(ISRestoreIndices(patch->allIntFacets, &intFacetsArray));
2311     PetscCall(ISRestoreIndices(dofMap, &dofMapArray));
2312     PetscCall(ISDestroy(&dofMap));
2313   }
2314   PetscCall(DMDestroy(&dm));
2315   PetscCall(PetscLogEventEnd(PC_Patch_ComputeOp, pc, 0, 0, 0));
2316 
2317   PetscFunctionReturn(PETSC_SUCCESS);
2318 }
2319 
2320 PetscErrorCode PCPatch_ScatterLocal_Private(PC pc, PetscInt p, Vec x, Vec y, InsertMode mode, ScatterMode scat, PatchScatterType scattertype)
2321 {
2322   PC_PATCH          *patch     = (PC_PATCH *)pc->data;
2323   const PetscScalar *xArray    = NULL;
2324   PetscScalar       *yArray    = NULL;
2325   const PetscInt    *gtolArray = NULL;
2326   PetscInt           dof, offset, lidx;
2327 
2328   PetscFunctionBeginHot;
2329   PetscCall(VecGetArrayRead(x, &xArray));
2330   PetscCall(VecGetArray(y, &yArray));
2331   if (scattertype == SCATTER_WITHARTIFICIAL) {
2332     PetscCall(PetscSectionGetDof(patch->gtolCountsWithArtificial, p, &dof));
2333     PetscCall(PetscSectionGetOffset(patch->gtolCountsWithArtificial, p, &offset));
2334     PetscCall(ISGetIndices(patch->gtolWithArtificial, &gtolArray));
2335   } else if (scattertype == SCATTER_WITHALL) {
2336     PetscCall(PetscSectionGetDof(patch->gtolCountsWithAll, p, &dof));
2337     PetscCall(PetscSectionGetOffset(patch->gtolCountsWithAll, p, &offset));
2338     PetscCall(ISGetIndices(patch->gtolWithAll, &gtolArray));
2339   } else {
2340     PetscCall(PetscSectionGetDof(patch->gtolCounts, p, &dof));
2341     PetscCall(PetscSectionGetOffset(patch->gtolCounts, p, &offset));
2342     PetscCall(ISGetIndices(patch->gtol, &gtolArray));
2343   }
2344   PetscCheck(mode != INSERT_VALUES || scat == SCATTER_FORWARD, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Can't insert if not scattering forward");
2345   PetscCheck(mode != ADD_VALUES || scat == SCATTER_REVERSE, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Can't add if not scattering reverse");
2346   for (lidx = 0; lidx < dof; ++lidx) {
2347     const PetscInt gidx = gtolArray[offset + lidx];
2348 
2349     if (mode == INSERT_VALUES) yArray[lidx] = xArray[gidx]; /* Forward */
2350     else yArray[gidx] += xArray[lidx];                      /* Reverse */
2351   }
2352   if (scattertype == SCATTER_WITHARTIFICIAL) {
2353     PetscCall(ISRestoreIndices(patch->gtolWithArtificial, &gtolArray));
2354   } else if (scattertype == SCATTER_WITHALL) {
2355     PetscCall(ISRestoreIndices(patch->gtolWithAll, &gtolArray));
2356   } else {
2357     PetscCall(ISRestoreIndices(patch->gtol, &gtolArray));
2358   }
2359   PetscCall(VecRestoreArrayRead(x, &xArray));
2360   PetscCall(VecRestoreArray(y, &yArray));
2361   PetscFunctionReturn(PETSC_SUCCESS);
2362 }
2363 
2364 static PetscErrorCode PCSetUp_PATCH_Linear(PC pc)
2365 {
2366   PC_PATCH   *patch = (PC_PATCH *)pc->data;
2367   const char *prefix;
2368   PetscInt    i;
2369 
2370   PetscFunctionBegin;
2371   if (!pc->setupcalled) {
2372     PetscCheck(patch->save_operators || !patch->denseinverse, PetscObjectComm((PetscObject)pc), PETSC_ERR_ARG_WRONGSTATE, "Can't have dense inverse without save operators");
2373     if (!patch->denseinverse) {
2374       PetscCall(PetscMalloc1(patch->npatch, &patch->solver));
2375       PetscCall(PCGetOptionsPrefix(pc, &prefix));
2376       for (i = 0; i < patch->npatch; ++i) {
2377         KSP ksp;
2378         PC  subpc;
2379 
2380         PetscCall(KSPCreate(PETSC_COMM_SELF, &ksp));
2381         PetscCall(KSPSetNestLevel(ksp, pc->kspnestlevel));
2382         PetscCall(KSPSetErrorIfNotConverged(ksp, pc->erroriffailure));
2383         PetscCall(KSPSetOptionsPrefix(ksp, prefix));
2384         PetscCall(KSPAppendOptionsPrefix(ksp, "sub_"));
2385         PetscCall(PetscObjectIncrementTabLevel((PetscObject)ksp, (PetscObject)pc, 1));
2386         PetscCall(KSPGetPC(ksp, &subpc));
2387         PetscCall(PetscObjectIncrementTabLevel((PetscObject)subpc, (PetscObject)pc, 1));
2388         patch->solver[i] = (PetscObject)ksp;
2389       }
2390     }
2391   }
2392   if (patch->save_operators) {
2393     if (patch->precomputeElementTensors) PetscCall(PCPatchPrecomputePatchTensors_Private(pc));
2394     for (i = 0; i < patch->npatch; ++i) {
2395       PetscCall(PCPatchComputeOperator_Internal(pc, NULL, patch->mat[i], i, PETSC_FALSE));
2396       if (!patch->denseinverse) {
2397         PetscCall(KSPSetOperators((KSP)patch->solver[i], patch->mat[i], patch->mat[i]));
2398       } else if (patch->mat[i] && !patch->densesolve) {
2399         /* Setup matmult callback */
2400         PetscCall(MatGetOperation(patch->mat[i], MATOP_MULT, (void (**)(void)) & patch->densesolve));
2401       }
2402     }
2403   }
2404   if (patch->local_composition_type == PC_COMPOSITE_MULTIPLICATIVE) {
2405     for (i = 0; i < patch->npatch; ++i) {
2406       /* Instead of padding patch->patchUpdate with zeros to get */
2407       /* patch->patchUpdateWithArtificial and then multiplying with the matrix, */
2408       /* just get rid of the columns that correspond to the dofs with */
2409       /* artificial bcs. That's of course fairly inefficient, hopefully we */
2410       /* can just assemble the rectangular matrix in the first place. */
2411       Mat      matSquare;
2412       IS       rowis;
2413       PetscInt dof;
2414 
2415       PetscCall(MatGetSize(patch->mat[i], &dof, NULL));
2416       if (dof == 0) {
2417         patch->matWithArtificial[i] = NULL;
2418         continue;
2419       }
2420 
2421       PetscCall(PCPatchCreateMatrix_Private(pc, i, &matSquare, PETSC_TRUE));
2422       PetscCall(PCPatchComputeOperator_Internal(pc, NULL, matSquare, i, PETSC_TRUE));
2423 
2424       PetscCall(MatGetSize(matSquare, &dof, NULL));
2425       PetscCall(ISCreateStride(PETSC_COMM_SELF, dof, 0, 1, &rowis));
2426       if (pc->setupcalled) {
2427         PetscCall(MatCreateSubMatrix(matSquare, rowis, patch->dofMappingWithoutToWithArtificial[i], MAT_REUSE_MATRIX, &patch->matWithArtificial[i]));
2428       } else {
2429         PetscCall(MatCreateSubMatrix(matSquare, rowis, patch->dofMappingWithoutToWithArtificial[i], MAT_INITIAL_MATRIX, &patch->matWithArtificial[i]));
2430       }
2431       PetscCall(ISDestroy(&rowis));
2432       PetscCall(MatDestroy(&matSquare));
2433     }
2434   }
2435   PetscFunctionReturn(PETSC_SUCCESS);
2436 }
2437 
2438 static PetscErrorCode PCSetUp_PATCH(PC pc)
2439 {
2440   PC_PATCH *patch = (PC_PATCH *)pc->data;
2441   PetscInt  i;
2442   PetscBool isNonlinear;
2443   PetscInt  maxDof = -1, maxDofWithArtificial = -1;
2444 
2445   PetscFunctionBegin;
2446   if (!pc->setupcalled) {
2447     PetscInt pStart, pEnd, p;
2448     PetscInt localSize;
2449 
2450     PetscCall(PetscLogEventBegin(PC_Patch_CreatePatches, pc, 0, 0, 0));
2451 
2452     isNonlinear = patch->isNonlinear;
2453     if (!patch->nsubspaces) {
2454       DM           dm, plex;
2455       PetscSection s;
2456       PetscInt     cStart, cEnd, c, Nf, f, numGlobalBcs = 0, *globalBcs, *Nb, **cellDofs;
2457 
2458       PetscCall(PCGetDM(pc, &dm));
2459       PetscCheck(dm, PetscObjectComm((PetscObject)pc), PETSC_ERR_ARG_WRONG, "Must set DM for PCPATCH or call PCPatchSetDiscretisationInfo()");
2460       PetscCall(DMConvert(dm, DMPLEX, &plex));
2461       dm = plex;
2462       PetscCall(DMGetLocalSection(dm, &s));
2463       PetscCall(PetscSectionGetNumFields(s, &Nf));
2464       PetscCall(PetscSectionGetChart(s, &pStart, &pEnd));
2465       for (p = pStart; p < pEnd; ++p) {
2466         PetscInt cdof;
2467         PetscCall(PetscSectionGetConstraintDof(s, p, &cdof));
2468         numGlobalBcs += cdof;
2469       }
2470       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
2471       PetscCall(PetscMalloc3(Nf, &Nb, Nf, &cellDofs, numGlobalBcs, &globalBcs));
2472       for (f = 0; f < Nf; ++f) {
2473         PetscFE        fe;
2474         PetscDualSpace sp;
2475         PetscInt       cdoff = 0;
2476 
2477         PetscCall(DMGetField(dm, f, NULL, (PetscObject *)&fe));
2478         /* PetscCall(PetscFEGetNumComponents(fe, &Nc[f])); */
2479         PetscCall(PetscFEGetDualSpace(fe, &sp));
2480         PetscCall(PetscDualSpaceGetDimension(sp, &Nb[f]));
2481 
2482         PetscCall(PetscMalloc1((cEnd - cStart) * Nb[f], &cellDofs[f]));
2483         for (c = cStart; c < cEnd; ++c) {
2484           PetscInt *closure = NULL;
2485           PetscInt  clSize  = 0, cl;
2486 
2487           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
2488           for (cl = 0; cl < clSize * 2; cl += 2) {
2489             const PetscInt p = closure[cl];
2490             PetscInt       fdof, d, foff;
2491 
2492             PetscCall(PetscSectionGetFieldDof(s, p, f, &fdof));
2493             PetscCall(PetscSectionGetFieldOffset(s, p, f, &foff));
2494             for (d = 0; d < fdof; ++d, ++cdoff) cellDofs[f][cdoff] = foff + d;
2495           }
2496           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
2497         }
2498         PetscCheck(cdoff == (cEnd - cStart) * Nb[f], PetscObjectComm((PetscObject)pc), PETSC_ERR_ARG_SIZ, "Total number of cellDofs %" PetscInt_FMT " for field %" PetscInt_FMT " should be Nc (%" PetscInt_FMT ") * cellDof (%" PetscInt_FMT ")", cdoff, f, cEnd - cStart, Nb[f]);
2499       }
2500       numGlobalBcs = 0;
2501       for (p = pStart; p < pEnd; ++p) {
2502         const PetscInt *ind;
2503         PetscInt        off, cdof, d;
2504 
2505         PetscCall(PetscSectionGetOffset(s, p, &off));
2506         PetscCall(PetscSectionGetConstraintDof(s, p, &cdof));
2507         PetscCall(PetscSectionGetConstraintIndices(s, p, &ind));
2508         for (d = 0; d < cdof; ++d) globalBcs[numGlobalBcs++] = off + ind[d];
2509       }
2510 
2511       PetscCall(PCPatchSetDiscretisationInfoCombined(pc, dm, Nb, (const PetscInt **)cellDofs, numGlobalBcs, globalBcs, numGlobalBcs, globalBcs));
2512       for (f = 0; f < Nf; ++f) PetscCall(PetscFree(cellDofs[f]));
2513       PetscCall(PetscFree3(Nb, cellDofs, globalBcs));
2514       PetscCall(PCPatchSetComputeFunction(pc, PCPatchComputeFunction_DMPlex_Private, NULL));
2515       PetscCall(PCPatchSetComputeOperator(pc, PCPatchComputeOperator_DMPlex_Private, NULL));
2516       PetscCall(DMDestroy(&dm));
2517     }
2518 
2519     localSize = patch->subspaceOffsets[patch->nsubspaces];
2520     PetscCall(VecCreateSeq(PETSC_COMM_SELF, localSize, &patch->localRHS));
2521     PetscCall(VecSetUp(patch->localRHS));
2522     PetscCall(VecDuplicate(patch->localRHS, &patch->localUpdate));
2523     PetscCall(PCPatchCreateCellPatches(pc));
2524     PetscCall(PCPatchCreateCellPatchDiscretisationInfo(pc));
2525 
2526     /* OK, now build the work vectors */
2527     PetscCall(PetscSectionGetChart(patch->gtolCounts, &pStart, &pEnd));
2528 
2529     if (patch->local_composition_type == PC_COMPOSITE_MULTIPLICATIVE) PetscCall(PetscMalloc1(patch->npatch, &patch->dofMappingWithoutToWithArtificial));
2530     if (isNonlinear) PetscCall(PetscMalloc1(patch->npatch, &patch->dofMappingWithoutToWithAll));
2531     for (p = pStart; p < pEnd; ++p) {
2532       PetscInt dof;
2533 
2534       PetscCall(PetscSectionGetDof(patch->gtolCounts, p, &dof));
2535       maxDof = PetscMax(maxDof, dof);
2536       if (patch->local_composition_type == PC_COMPOSITE_MULTIPLICATIVE) {
2537         const PetscInt *gtolArray, *gtolArrayWithArtificial = NULL;
2538         PetscInt        numPatchDofs, offset;
2539         PetscInt        numPatchDofsWithArtificial, offsetWithArtificial;
2540         PetscInt        dofWithoutArtificialCounter = 0;
2541         PetscInt       *patchWithoutArtificialToWithArtificialArray;
2542 
2543         PetscCall(PetscSectionGetDof(patch->gtolCountsWithArtificial, p, &dof));
2544         maxDofWithArtificial = PetscMax(maxDofWithArtificial, dof);
2545 
2546         /* Now build the mapping that for a dof in a patch WITHOUT dofs that have artificial bcs gives the */
2547         /* the index in the patch with all dofs */
2548         PetscCall(ISGetIndices(patch->gtol, &gtolArray));
2549 
2550         PetscCall(PetscSectionGetDof(patch->gtolCounts, p, &numPatchDofs));
2551         if (numPatchDofs == 0) {
2552           patch->dofMappingWithoutToWithArtificial[p - pStart] = NULL;
2553           continue;
2554         }
2555 
2556         PetscCall(PetscSectionGetOffset(patch->gtolCounts, p, &offset));
2557         PetscCall(ISGetIndices(patch->gtolWithArtificial, &gtolArrayWithArtificial));
2558         PetscCall(PetscSectionGetDof(patch->gtolCountsWithArtificial, p, &numPatchDofsWithArtificial));
2559         PetscCall(PetscSectionGetOffset(patch->gtolCountsWithArtificial, p, &offsetWithArtificial));
2560 
2561         PetscCall(PetscMalloc1(numPatchDofs, &patchWithoutArtificialToWithArtificialArray));
2562         for (i = 0; i < numPatchDofsWithArtificial; i++) {
2563           if (gtolArrayWithArtificial[i + offsetWithArtificial] == gtolArray[offset + dofWithoutArtificialCounter]) {
2564             patchWithoutArtificialToWithArtificialArray[dofWithoutArtificialCounter] = i;
2565             dofWithoutArtificialCounter++;
2566             if (dofWithoutArtificialCounter == numPatchDofs) break;
2567           }
2568         }
2569         PetscCall(ISCreateGeneral(PETSC_COMM_SELF, numPatchDofs, patchWithoutArtificialToWithArtificialArray, PETSC_OWN_POINTER, &patch->dofMappingWithoutToWithArtificial[p - pStart]));
2570         PetscCall(ISRestoreIndices(patch->gtol, &gtolArray));
2571         PetscCall(ISRestoreIndices(patch->gtolWithArtificial, &gtolArrayWithArtificial));
2572       }
2573     }
2574     for (p = pStart; p < pEnd; ++p) {
2575       if (isNonlinear) {
2576         const PetscInt *gtolArray, *gtolArrayWithAll = NULL;
2577         PetscInt        numPatchDofs, offset;
2578         PetscInt        numPatchDofsWithAll, offsetWithAll;
2579         PetscInt        dofWithoutAllCounter = 0;
2580         PetscInt       *patchWithoutAllToWithAllArray;
2581 
2582         /* Now build the mapping that for a dof in a patch WITHOUT dofs that have artificial bcs gives the */
2583         /* the index in the patch with all dofs */
2584         PetscCall(ISGetIndices(patch->gtol, &gtolArray));
2585 
2586         PetscCall(PetscSectionGetDof(patch->gtolCounts, p, &numPatchDofs));
2587         if (numPatchDofs == 0) {
2588           patch->dofMappingWithoutToWithAll[p - pStart] = NULL;
2589           continue;
2590         }
2591 
2592         PetscCall(PetscSectionGetOffset(patch->gtolCounts, p, &offset));
2593         PetscCall(ISGetIndices(patch->gtolWithAll, &gtolArrayWithAll));
2594         PetscCall(PetscSectionGetDof(patch->gtolCountsWithAll, p, &numPatchDofsWithAll));
2595         PetscCall(PetscSectionGetOffset(patch->gtolCountsWithAll, p, &offsetWithAll));
2596 
2597         PetscCall(PetscMalloc1(numPatchDofs, &patchWithoutAllToWithAllArray));
2598 
2599         for (i = 0; i < numPatchDofsWithAll; i++) {
2600           if (gtolArrayWithAll[i + offsetWithAll] == gtolArray[offset + dofWithoutAllCounter]) {
2601             patchWithoutAllToWithAllArray[dofWithoutAllCounter] = i;
2602             dofWithoutAllCounter++;
2603             if (dofWithoutAllCounter == numPatchDofs) break;
2604           }
2605         }
2606         PetscCall(ISCreateGeneral(PETSC_COMM_SELF, numPatchDofs, patchWithoutAllToWithAllArray, PETSC_OWN_POINTER, &patch->dofMappingWithoutToWithAll[p - pStart]));
2607         PetscCall(ISRestoreIndices(patch->gtol, &gtolArray));
2608         PetscCall(ISRestoreIndices(patch->gtolWithAll, &gtolArrayWithAll));
2609       }
2610     }
2611     if (patch->local_composition_type == PC_COMPOSITE_MULTIPLICATIVE) {
2612       PetscCall(VecCreateSeq(PETSC_COMM_SELF, maxDofWithArtificial, &patch->patchRHSWithArtificial));
2613       PetscCall(VecSetUp(patch->patchRHSWithArtificial));
2614     }
2615     PetscCall(VecCreateSeq(PETSC_COMM_SELF, maxDof, &patch->patchRHS));
2616     PetscCall(VecSetUp(patch->patchRHS));
2617     PetscCall(VecCreateSeq(PETSC_COMM_SELF, maxDof, &patch->patchUpdate));
2618     PetscCall(VecSetUp(patch->patchUpdate));
2619     if (patch->save_operators) {
2620       PetscCall(PetscMalloc1(patch->npatch, &patch->mat));
2621       for (i = 0; i < patch->npatch; ++i) PetscCall(PCPatchCreateMatrix_Private(pc, i, &patch->mat[i], PETSC_FALSE));
2622     }
2623     PetscCall(PetscLogEventEnd(PC_Patch_CreatePatches, pc, 0, 0, 0));
2624 
2625     /* If desired, calculate weights for dof multiplicity */
2626     if (patch->partition_of_unity) {
2627       PetscScalar *input  = NULL;
2628       PetscScalar *output = NULL;
2629       Vec          global;
2630 
2631       PetscCall(VecDuplicate(patch->localRHS, &patch->dof_weights));
2632       if (patch->local_composition_type == PC_COMPOSITE_ADDITIVE) {
2633         for (i = 0; i < patch->npatch; ++i) {
2634           PetscInt dof;
2635 
2636           PetscCall(PetscSectionGetDof(patch->gtolCounts, i + pStart, &dof));
2637           if (dof <= 0) continue;
2638           PetscCall(VecSet(patch->patchRHS, 1.0));
2639           PetscCall(PCPatch_ScatterLocal_Private(pc, i + pStart, patch->patchRHS, patch->dof_weights, ADD_VALUES, SCATTER_REVERSE, SCATTER_INTERIOR));
2640         }
2641       } else {
2642         /* multiplicative is actually only locally multiplicative and globally additive. need the pou where the mesh decomposition overlaps */
2643         PetscCall(VecSet(patch->dof_weights, 1.0));
2644       }
2645 
2646       PetscCall(VecDuplicate(patch->dof_weights, &global));
2647       PetscCall(VecSet(global, 0.));
2648 
2649       PetscCall(VecGetArray(patch->dof_weights, &input));
2650       PetscCall(VecGetArray(global, &output));
2651       PetscCall(PetscSFReduceBegin(patch->sectionSF, MPIU_SCALAR, input, output, MPI_SUM));
2652       PetscCall(PetscSFReduceEnd(patch->sectionSF, MPIU_SCALAR, input, output, MPI_SUM));
2653       PetscCall(VecRestoreArray(patch->dof_weights, &input));
2654       PetscCall(VecRestoreArray(global, &output));
2655 
2656       PetscCall(VecReciprocal(global));
2657 
2658       PetscCall(VecGetArray(patch->dof_weights, &output));
2659       PetscCall(VecGetArray(global, &input));
2660       PetscCall(PetscSFBcastBegin(patch->sectionSF, MPIU_SCALAR, input, output, MPI_REPLACE));
2661       PetscCall(PetscSFBcastEnd(patch->sectionSF, MPIU_SCALAR, input, output, MPI_REPLACE));
2662       PetscCall(VecRestoreArray(patch->dof_weights, &output));
2663       PetscCall(VecRestoreArray(global, &input));
2664       PetscCall(VecDestroy(&global));
2665     }
2666     if (patch->local_composition_type == PC_COMPOSITE_MULTIPLICATIVE && patch->save_operators && !patch->isNonlinear) PetscCall(PetscMalloc1(patch->npatch, &patch->matWithArtificial));
2667   }
2668   PetscCall((*patch->setupsolver)(pc));
2669   PetscFunctionReturn(PETSC_SUCCESS);
2670 }
2671 
2672 static PetscErrorCode PCApply_PATCH_Linear(PC pc, PetscInt i, Vec x, Vec y)
2673 {
2674   PC_PATCH *patch = (PC_PATCH *)pc->data;
2675   KSP       ksp;
2676   Mat       op;
2677   PetscInt  m, n;
2678 
2679   PetscFunctionBegin;
2680   if (patch->denseinverse) {
2681     PetscCall((*patch->densesolve)(patch->mat[i], x, y));
2682     PetscFunctionReturn(PETSC_SUCCESS);
2683   }
2684   ksp = (KSP)patch->solver[i];
2685   if (!patch->save_operators) {
2686     Mat mat;
2687 
2688     PetscCall(PCPatchCreateMatrix_Private(pc, i, &mat, PETSC_FALSE));
2689     /* Populate operator here. */
2690     PetscCall(PCPatchComputeOperator_Internal(pc, NULL, mat, i, PETSC_FALSE));
2691     PetscCall(KSPSetOperators(ksp, mat, mat));
2692     /* Drop reference so the KSPSetOperators below will blow it away. */
2693     PetscCall(MatDestroy(&mat));
2694   }
2695   PetscCall(PetscLogEventBegin(PC_Patch_Solve, pc, 0, 0, 0));
2696   if (!ksp->setfromoptionscalled) PetscCall(KSPSetFromOptions(ksp));
2697   /* Disgusting trick to reuse work vectors */
2698   PetscCall(KSPGetOperators(ksp, &op, NULL));
2699   PetscCall(MatGetLocalSize(op, &m, &n));
2700   x->map->n = m;
2701   y->map->n = n;
2702   x->map->N = m;
2703   y->map->N = n;
2704   PetscCall(KSPSolve(ksp, x, y));
2705   PetscCall(KSPCheckSolve(ksp, pc, y));
2706   PetscCall(PetscLogEventEnd(PC_Patch_Solve, pc, 0, 0, 0));
2707   if (!patch->save_operators) {
2708     PC pc;
2709     PetscCall(KSPSetOperators(ksp, NULL, NULL));
2710     PetscCall(KSPGetPC(ksp, &pc));
2711     /* Destroy PC context too, otherwise the factored matrix hangs around. */
2712     PetscCall(PCReset(pc));
2713   }
2714   PetscFunctionReturn(PETSC_SUCCESS);
2715 }
2716 
2717 static PetscErrorCode PCUpdateMultiplicative_PATCH_Linear(PC pc, PetscInt i, PetscInt pStart)
2718 {
2719   PC_PATCH *patch = (PC_PATCH *)pc->data;
2720   Mat       multMat;
2721   PetscInt  n, m;
2722 
2723   PetscFunctionBegin;
2724 
2725   if (patch->save_operators) {
2726     multMat = patch->matWithArtificial[i];
2727   } else {
2728     /*Very inefficient, hopefully we can just assemble the rectangular matrix in the first place.*/
2729     Mat      matSquare;
2730     PetscInt dof;
2731     IS       rowis;
2732     PetscCall(PCPatchCreateMatrix_Private(pc, i, &matSquare, PETSC_TRUE));
2733     PetscCall(PCPatchComputeOperator_Internal(pc, NULL, matSquare, i, PETSC_TRUE));
2734     PetscCall(MatGetSize(matSquare, &dof, NULL));
2735     PetscCall(ISCreateStride(PETSC_COMM_SELF, dof, 0, 1, &rowis));
2736     PetscCall(MatCreateSubMatrix(matSquare, rowis, patch->dofMappingWithoutToWithArtificial[i], MAT_INITIAL_MATRIX, &multMat));
2737     PetscCall(MatDestroy(&matSquare));
2738     PetscCall(ISDestroy(&rowis));
2739   }
2740   /* Disgusting trick to reuse work vectors */
2741   PetscCall(MatGetLocalSize(multMat, &m, &n));
2742   patch->patchUpdate->map->n            = n;
2743   patch->patchRHSWithArtificial->map->n = m;
2744   patch->patchUpdate->map->N            = n;
2745   patch->patchRHSWithArtificial->map->N = m;
2746   PetscCall(MatMult(multMat, patch->patchUpdate, patch->patchRHSWithArtificial));
2747   PetscCall(VecScale(patch->patchRHSWithArtificial, -1.0));
2748   PetscCall(PCPatch_ScatterLocal_Private(pc, i + pStart, patch->patchRHSWithArtificial, patch->localRHS, ADD_VALUES, SCATTER_REVERSE, SCATTER_WITHARTIFICIAL));
2749   if (!patch->save_operators) PetscCall(MatDestroy(&multMat));
2750   PetscFunctionReturn(PETSC_SUCCESS);
2751 }
2752 
2753 static PetscErrorCode PCApply_PATCH(PC pc, Vec x, Vec y)
2754 {
2755   PC_PATCH          *patch        = (PC_PATCH *)pc->data;
2756   const PetscScalar *globalRHS    = NULL;
2757   PetscScalar       *localRHS     = NULL;
2758   PetscScalar       *globalUpdate = NULL;
2759   const PetscInt    *bcNodes      = NULL;
2760   PetscInt           nsweep       = patch->symmetrise_sweep ? 2 : 1;
2761   PetscInt           start[2]     = {0, 0};
2762   PetscInt           end[2]       = {-1, -1};
2763   const PetscInt     inc[2]       = {1, -1};
2764   const PetscScalar *localUpdate;
2765   const PetscInt    *iterationSet;
2766   PetscInt           pStart, numBcs, n, sweep, bc, j;
2767 
2768   PetscFunctionBegin;
2769   PetscCall(PetscLogEventBegin(PC_Patch_Apply, pc, 0, 0, 0));
2770   PetscCall(PetscOptionsPushGetViewerOff(PETSC_TRUE));
2771   /* start, end, inc have 2 entries to manage a second backward sweep if we symmetrize */
2772   end[0]   = patch->npatch;
2773   start[1] = patch->npatch - 1;
2774   if (patch->user_patches) {
2775     PetscCall(ISGetLocalSize(patch->iterationSet, &end[0]));
2776     start[1] = end[0] - 1;
2777     PetscCall(ISGetIndices(patch->iterationSet, &iterationSet));
2778   }
2779   /* Scatter from global space into overlapped local spaces */
2780   PetscCall(VecGetArrayRead(x, &globalRHS));
2781   PetscCall(VecGetArray(patch->localRHS, &localRHS));
2782   PetscCall(PetscSFBcastBegin(patch->sectionSF, MPIU_SCALAR, globalRHS, localRHS, MPI_REPLACE));
2783   PetscCall(PetscSFBcastEnd(patch->sectionSF, MPIU_SCALAR, globalRHS, localRHS, MPI_REPLACE));
2784   PetscCall(VecRestoreArrayRead(x, &globalRHS));
2785   PetscCall(VecRestoreArray(patch->localRHS, &localRHS));
2786 
2787   PetscCall(VecSet(patch->localUpdate, 0.0));
2788   PetscCall(PetscSectionGetChart(patch->gtolCounts, &pStart, NULL));
2789   PetscCall(PetscLogEventBegin(PC_Patch_Solve, pc, 0, 0, 0));
2790   for (sweep = 0; sweep < nsweep; sweep++) {
2791     for (j = start[sweep]; j * inc[sweep] < end[sweep] * inc[sweep]; j += inc[sweep]) {
2792       PetscInt i = patch->user_patches ? iterationSet[j] : j;
2793       PetscInt start, len;
2794 
2795       PetscCall(PetscSectionGetDof(patch->gtolCounts, i + pStart, &len));
2796       PetscCall(PetscSectionGetOffset(patch->gtolCounts, i + pStart, &start));
2797       /* TODO: Squash out these guys in the setup as well. */
2798       if (len <= 0) continue;
2799       /* TODO: Do we need different scatters for X and Y? */
2800       PetscCall(PCPatch_ScatterLocal_Private(pc, i + pStart, patch->localRHS, patch->patchRHS, INSERT_VALUES, SCATTER_FORWARD, SCATTER_INTERIOR));
2801       PetscCall((*patch->applysolver)(pc, i, patch->patchRHS, patch->patchUpdate));
2802       PetscCall(PCPatch_ScatterLocal_Private(pc, i + pStart, patch->patchUpdate, patch->localUpdate, ADD_VALUES, SCATTER_REVERSE, SCATTER_INTERIOR));
2803       if (patch->local_composition_type == PC_COMPOSITE_MULTIPLICATIVE) PetscCall((*patch->updatemultiplicative)(pc, i, pStart));
2804     }
2805   }
2806   PetscCall(PetscLogEventEnd(PC_Patch_Solve, pc, 0, 0, 0));
2807   if (patch->user_patches) PetscCall(ISRestoreIndices(patch->iterationSet, &iterationSet));
2808   /* XXX: should we do this on the global vector? */
2809   if (patch->partition_of_unity) PetscCall(VecPointwiseMult(patch->localUpdate, patch->localUpdate, patch->dof_weights));
2810   /* Now patch->localUpdate contains the solution of the patch solves, so we need to combine them all. */
2811   PetscCall(VecSet(y, 0.0));
2812   PetscCall(VecGetArray(y, &globalUpdate));
2813   PetscCall(VecGetArrayRead(patch->localUpdate, &localUpdate));
2814   PetscCall(PetscSFReduceBegin(patch->sectionSF, MPIU_SCALAR, localUpdate, globalUpdate, MPI_SUM));
2815   PetscCall(PetscSFReduceEnd(patch->sectionSF, MPIU_SCALAR, localUpdate, globalUpdate, MPI_SUM));
2816   PetscCall(VecRestoreArrayRead(patch->localUpdate, &localUpdate));
2817 
2818   /* Now we need to send the global BC values through */
2819   PetscCall(VecGetArrayRead(x, &globalRHS));
2820   PetscCall(ISGetSize(patch->globalBcNodes, &numBcs));
2821   PetscCall(ISGetIndices(patch->globalBcNodes, &bcNodes));
2822   PetscCall(VecGetLocalSize(x, &n));
2823   for (bc = 0; bc < numBcs; ++bc) {
2824     const PetscInt idx = bcNodes[bc];
2825     if (idx < n) globalUpdate[idx] = globalRHS[idx];
2826   }
2827 
2828   PetscCall(ISRestoreIndices(patch->globalBcNodes, &bcNodes));
2829   PetscCall(VecRestoreArrayRead(x, &globalRHS));
2830   PetscCall(VecRestoreArray(y, &globalUpdate));
2831 
2832   PetscCall(PetscOptionsPopGetViewerOff());
2833   PetscCall(PetscLogEventEnd(PC_Patch_Apply, pc, 0, 0, 0));
2834   PetscFunctionReturn(PETSC_SUCCESS);
2835 }
2836 
2837 static PetscErrorCode PCReset_PATCH_Linear(PC pc)
2838 {
2839   PC_PATCH *patch = (PC_PATCH *)pc->data;
2840   PetscInt  i;
2841 
2842   PetscFunctionBegin;
2843   if (patch->solver) {
2844     for (i = 0; i < patch->npatch; ++i) PetscCall(KSPReset((KSP)patch->solver[i]));
2845   }
2846   PetscFunctionReturn(PETSC_SUCCESS);
2847 }
2848 
2849 static PetscErrorCode PCReset_PATCH(PC pc)
2850 {
2851   PC_PATCH *patch = (PC_PATCH *)pc->data;
2852   PetscInt  i;
2853 
2854   PetscFunctionBegin;
2855 
2856   PetscCall(PetscSFDestroy(&patch->sectionSF));
2857   PetscCall(PetscSectionDestroy(&patch->cellCounts));
2858   PetscCall(PetscSectionDestroy(&patch->pointCounts));
2859   PetscCall(PetscSectionDestroy(&patch->cellNumbering));
2860   PetscCall(PetscSectionDestroy(&patch->gtolCounts));
2861   PetscCall(ISDestroy(&patch->gtol));
2862   PetscCall(ISDestroy(&patch->cells));
2863   PetscCall(ISDestroy(&patch->points));
2864   PetscCall(ISDestroy(&patch->dofs));
2865   PetscCall(ISDestroy(&patch->offs));
2866   PetscCall(PetscSectionDestroy(&patch->patchSection));
2867   PetscCall(ISDestroy(&patch->ghostBcNodes));
2868   PetscCall(ISDestroy(&patch->globalBcNodes));
2869   PetscCall(PetscSectionDestroy(&patch->gtolCountsWithArtificial));
2870   PetscCall(ISDestroy(&patch->gtolWithArtificial));
2871   PetscCall(ISDestroy(&patch->dofsWithArtificial));
2872   PetscCall(ISDestroy(&patch->offsWithArtificial));
2873   PetscCall(PetscSectionDestroy(&patch->gtolCountsWithAll));
2874   PetscCall(ISDestroy(&patch->gtolWithAll));
2875   PetscCall(ISDestroy(&patch->dofsWithAll));
2876   PetscCall(ISDestroy(&patch->offsWithAll));
2877   PetscCall(VecDestroy(&patch->cellMats));
2878   PetscCall(VecDestroy(&patch->intFacetMats));
2879   PetscCall(ISDestroy(&patch->allCells));
2880   PetscCall(ISDestroy(&patch->intFacets));
2881   PetscCall(ISDestroy(&patch->extFacets));
2882   PetscCall(ISDestroy(&patch->intFacetsToPatchCell));
2883   PetscCall(PetscSectionDestroy(&patch->intFacetCounts));
2884   PetscCall(PetscSectionDestroy(&patch->extFacetCounts));
2885 
2886   if (patch->dofSection)
2887     for (i = 0; i < patch->nsubspaces; i++) PetscCall(PetscSectionDestroy(&patch->dofSection[i]));
2888   PetscCall(PetscFree(patch->dofSection));
2889   PetscCall(PetscFree(patch->bs));
2890   PetscCall(PetscFree(patch->nodesPerCell));
2891   if (patch->cellNodeMap)
2892     for (i = 0; i < patch->nsubspaces; i++) PetscCall(PetscFree(patch->cellNodeMap[i]));
2893   PetscCall(PetscFree(patch->cellNodeMap));
2894   PetscCall(PetscFree(patch->subspaceOffsets));
2895 
2896   PetscCall((*patch->resetsolver)(pc));
2897 
2898   if (patch->subspaces_to_exclude) PetscCall(PetscHSetIDestroy(&patch->subspaces_to_exclude));
2899 
2900   PetscCall(VecDestroy(&patch->localRHS));
2901   PetscCall(VecDestroy(&patch->localUpdate));
2902   PetscCall(VecDestroy(&patch->patchRHS));
2903   PetscCall(VecDestroy(&patch->patchUpdate));
2904   PetscCall(VecDestroy(&patch->dof_weights));
2905   if (patch->patch_dof_weights) {
2906     for (i = 0; i < patch->npatch; ++i) PetscCall(VecDestroy(&patch->patch_dof_weights[i]));
2907     PetscCall(PetscFree(patch->patch_dof_weights));
2908   }
2909   if (patch->mat) {
2910     for (i = 0; i < patch->npatch; ++i) PetscCall(MatDestroy(&patch->mat[i]));
2911     PetscCall(PetscFree(patch->mat));
2912   }
2913   if (patch->matWithArtificial && !patch->isNonlinear) {
2914     for (i = 0; i < patch->npatch; ++i) PetscCall(MatDestroy(&patch->matWithArtificial[i]));
2915     PetscCall(PetscFree(patch->matWithArtificial));
2916   }
2917   PetscCall(VecDestroy(&patch->patchRHSWithArtificial));
2918   if (patch->dofMappingWithoutToWithArtificial) {
2919     for (i = 0; i < patch->npatch; ++i) PetscCall(ISDestroy(&patch->dofMappingWithoutToWithArtificial[i]));
2920     PetscCall(PetscFree(patch->dofMappingWithoutToWithArtificial));
2921   }
2922   if (patch->dofMappingWithoutToWithAll) {
2923     for (i = 0; i < patch->npatch; ++i) PetscCall(ISDestroy(&patch->dofMappingWithoutToWithAll[i]));
2924     PetscCall(PetscFree(patch->dofMappingWithoutToWithAll));
2925   }
2926   PetscCall(PetscFree(patch->sub_mat_type));
2927   if (patch->userIS) {
2928     for (i = 0; i < patch->npatch; ++i) PetscCall(ISDestroy(&patch->userIS[i]));
2929     PetscCall(PetscFree(patch->userIS));
2930   }
2931   PetscCall(PetscFree(patch->precomputedTensorLocations));
2932   PetscCall(PetscFree(patch->precomputedIntFacetTensorLocations));
2933 
2934   patch->bs          = NULL;
2935   patch->cellNodeMap = NULL;
2936   patch->nsubspaces  = 0;
2937   PetscCall(ISDestroy(&patch->iterationSet));
2938 
2939   PetscCall(PetscOptionsRestoreViewer(&patch->viewerCells));
2940   PetscCall(PetscOptionsRestoreViewer(&patch->viewerIntFacets));
2941   PetscCall(PetscOptionsRestoreViewer(&patch->viewerPoints));
2942   PetscCall(PetscOptionsRestoreViewer(&patch->viewerSection));
2943   PetscCall(PetscOptionsRestoreViewer(&patch->viewerMatrix));
2944   PetscFunctionReturn(PETSC_SUCCESS);
2945 }
2946 
2947 static PetscErrorCode PCDestroy_PATCH_Linear(PC pc)
2948 {
2949   PC_PATCH *patch = (PC_PATCH *)pc->data;
2950   PetscInt  i;
2951 
2952   PetscFunctionBegin;
2953   if (patch->solver) {
2954     for (i = 0; i < patch->npatch; ++i) PetscCall(KSPDestroy((KSP *)&patch->solver[i]));
2955     PetscCall(PetscFree(patch->solver));
2956   }
2957   PetscFunctionReturn(PETSC_SUCCESS);
2958 }
2959 
2960 static PetscErrorCode PCDestroy_PATCH(PC pc)
2961 {
2962   PC_PATCH *patch = (PC_PATCH *)pc->data;
2963 
2964   PetscFunctionBegin;
2965   PetscCall(PCReset_PATCH(pc));
2966   PetscCall((*patch->destroysolver)(pc));
2967   PetscCall(PetscFree(pc->data));
2968   PetscFunctionReturn(PETSC_SUCCESS);
2969 }
2970 
2971 static PetscErrorCode PCSetFromOptions_PATCH(PC pc, PetscOptionItems *PetscOptionsObject)
2972 {
2973   PC_PATCH            *patch                 = (PC_PATCH *)pc->data;
2974   PCPatchConstructType patchConstructionType = PC_PATCH_STAR;
2975   char                 sub_mat_type[PETSC_MAX_PATH_LEN];
2976   char                 option[PETSC_MAX_PATH_LEN];
2977   const char          *prefix;
2978   PetscBool            flg, dimflg, codimflg;
2979   MPI_Comm             comm;
2980   PetscInt            *ifields, nfields, k;
2981   PCCompositeType      loctype = PC_COMPOSITE_ADDITIVE;
2982 
2983   PetscFunctionBegin;
2984   PetscCall(PetscObjectGetComm((PetscObject)pc, &comm));
2985   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)pc, &prefix));
2986   PetscOptionsHeadBegin(PetscOptionsObject, "Patch solver options");
2987 
2988   PetscCall(PetscSNPrintf(option, PETSC_MAX_PATH_LEN, "-%s_patch_save_operators", patch->classname));
2989   PetscCall(PetscOptionsBool(option, "Store all patch operators for lifetime of object?", "PCPatchSetSaveOperators", patch->save_operators, &patch->save_operators, &flg));
2990 
2991   PetscCall(PetscSNPrintf(option, PETSC_MAX_PATH_LEN, "-%s_patch_precompute_element_tensors", patch->classname));
2992   PetscCall(PetscOptionsBool(option, "Compute each element tensor only once?", "PCPatchSetPrecomputeElementTensors", patch->precomputeElementTensors, &patch->precomputeElementTensors, &flg));
2993   PetscCall(PetscSNPrintf(option, PETSC_MAX_PATH_LEN, "-%s_patch_partition_of_unity", patch->classname));
2994   PetscCall(PetscOptionsBool(option, "Weight contributions by dof multiplicity?", "PCPatchSetPartitionOfUnity", patch->partition_of_unity, &patch->partition_of_unity, &flg));
2995 
2996   PetscCall(PetscSNPrintf(option, PETSC_MAX_PATH_LEN, "-%s_patch_local_type", patch->classname));
2997   PetscCall(PetscOptionsEnum(option, "Type of local solver composition (additive or multiplicative)", "PCPatchSetLocalComposition", PCCompositeTypes, (PetscEnum)loctype, (PetscEnum *)&loctype, &flg));
2998   if (flg) PetscCall(PCPatchSetLocalComposition(pc, loctype));
2999   PetscCall(PetscSNPrintf(option, PETSC_MAX_PATH_LEN, "-%s_patch_dense_inverse", patch->classname));
3000   PetscCall(PetscOptionsBool(option, "Compute inverses of patch matrices and apply directly? Ignores KSP/PC settings on patch.", "PCPatchSetDenseInverse", patch->denseinverse, &patch->denseinverse, &flg));
3001   PetscCall(PetscSNPrintf(option, PETSC_MAX_PATH_LEN, "-%s_patch_construct_dim", patch->classname));
3002   PetscCall(PetscOptionsInt(option, "What dimension of mesh point to construct patches by? (0 = vertices)", "PCPATCH", patch->dim, &patch->dim, &dimflg));
3003   PetscCall(PetscSNPrintf(option, PETSC_MAX_PATH_LEN, "-%s_patch_construct_codim", patch->classname));
3004   PetscCall(PetscOptionsInt(option, "What co-dimension of mesh point to construct patches by? (0 = cells)", "PCPATCH", patch->codim, &patch->codim, &codimflg));
3005   PetscCheck(!dimflg || !codimflg, comm, PETSC_ERR_ARG_WRONG, "Can only set one of dimension or co-dimension");
3006 
3007   PetscCall(PetscSNPrintf(option, PETSC_MAX_PATH_LEN, "-%s_patch_construct_type", patch->classname));
3008   PetscCall(PetscOptionsEnum(option, "How should the patches be constructed?", "PCPatchSetConstructType", PCPatchConstructTypes, (PetscEnum)patchConstructionType, (PetscEnum *)&patchConstructionType, &flg));
3009   if (flg) PetscCall(PCPatchSetConstructType(pc, patchConstructionType, NULL, NULL));
3010 
3011   PetscCall(PetscSNPrintf(option, PETSC_MAX_PATH_LEN, "-%s_patch_vanka_dim", patch->classname));
3012   PetscCall(PetscOptionsInt(option, "Topological dimension of entities for Vanka to ignore", "PCPATCH", patch->vankadim, &patch->vankadim, &flg));
3013 
3014   PetscCall(PetscSNPrintf(option, PETSC_MAX_PATH_LEN, "-%s_patch_ignore_dim", patch->classname));
3015   PetscCall(PetscOptionsInt(option, "Topological dimension of entities for completion to ignore", "PCPATCH", patch->ignoredim, &patch->ignoredim, &flg));
3016 
3017   PetscCall(PetscSNPrintf(option, PETSC_MAX_PATH_LEN, "-%s_patch_pardecomp_overlap", patch->classname));
3018   PetscCall(PetscOptionsInt(option, "What overlap should we use in construct type pardecomp?", "PCPATCH", patch->pardecomp_overlap, &patch->pardecomp_overlap, &flg));
3019 
3020   PetscCall(PetscSNPrintf(option, PETSC_MAX_PATH_LEN, "-%s_patch_sub_mat_type", patch->classname));
3021   PetscCall(PetscOptionsFList(option, "Matrix type for patch solves", "PCPatchSetSubMatType", MatList, NULL, sub_mat_type, PETSC_MAX_PATH_LEN, &flg));
3022   if (flg) PetscCall(PCPatchSetSubMatType(pc, sub_mat_type));
3023 
3024   PetscCall(PetscSNPrintf(option, PETSC_MAX_PATH_LEN, "-%s_patch_symmetrise_sweep", patch->classname));
3025   PetscCall(PetscOptionsBool(option, "Go start->end, end->start?", "PCPATCH", patch->symmetrise_sweep, &patch->symmetrise_sweep, &flg));
3026 
3027   /* If the user has set the number of subspaces, use that for the buffer size,
3028    otherwise use a large number */
3029   if (patch->nsubspaces <= 0) {
3030     nfields = 128;
3031   } else {
3032     nfields = patch->nsubspaces;
3033   }
3034   PetscCall(PetscMalloc1(nfields, &ifields));
3035   PetscCall(PetscSNPrintf(option, PETSC_MAX_PATH_LEN, "-%s_patch_exclude_subspaces", patch->classname));
3036   PetscCall(PetscOptionsGetIntArray(((PetscObject)pc)->options, ((PetscObject)pc)->prefix, option, ifields, &nfields, &flg));
3037   PetscCheck(!flg || !(patchConstructionType == PC_PATCH_USER), comm, PETSC_ERR_ARG_INCOMP, "We cannot support excluding a subspace with user patches because we do not index patches with a mesh point");
3038   if (flg) {
3039     PetscCall(PetscHSetIClear(patch->subspaces_to_exclude));
3040     for (k = 0; k < nfields; k++) PetscCall(PetscHSetIAdd(patch->subspaces_to_exclude, ifields[k]));
3041   }
3042   PetscCall(PetscFree(ifields));
3043 
3044   PetscCall(PetscSNPrintf(option, PETSC_MAX_PATH_LEN, "-%s_patch_patches_view", patch->classname));
3045   PetscCall(PetscOptionsBool(option, "Print out information during patch construction", "PCPATCH", patch->viewPatches, &patch->viewPatches, &flg));
3046   PetscCall(PetscSNPrintf(option, PETSC_MAX_PATH_LEN, "-%s_patch_cells_view", patch->classname));
3047   PetscCall(PetscOptionsGetViewer(comm, ((PetscObject)pc)->options, prefix, option, &patch->viewerCells, &patch->formatCells, &patch->viewCells));
3048   PetscCall(PetscSNPrintf(option, PETSC_MAX_PATH_LEN, "-%s_patch_interior_facets_view", patch->classname));
3049   PetscCall(PetscOptionsGetViewer(comm, ((PetscObject)pc)->options, prefix, option, &patch->viewerIntFacets, &patch->formatIntFacets, &patch->viewIntFacets));
3050   PetscCall(PetscSNPrintf(option, PETSC_MAX_PATH_LEN, "-%s_patch_exterior_facets_view", patch->classname));
3051   PetscCall(PetscOptionsGetViewer(comm, ((PetscObject)pc)->options, prefix, option, &patch->viewerExtFacets, &patch->formatExtFacets, &patch->viewExtFacets));
3052   PetscCall(PetscSNPrintf(option, PETSC_MAX_PATH_LEN, "-%s_patch_points_view", patch->classname));
3053   PetscCall(PetscOptionsGetViewer(comm, ((PetscObject)pc)->options, prefix, option, &patch->viewerPoints, &patch->formatPoints, &patch->viewPoints));
3054   PetscCall(PetscSNPrintf(option, PETSC_MAX_PATH_LEN, "-%s_patch_section_view", patch->classname));
3055   PetscCall(PetscOptionsGetViewer(comm, ((PetscObject)pc)->options, prefix, option, &patch->viewerSection, &patch->formatSection, &patch->viewSection));
3056   PetscCall(PetscSNPrintf(option, PETSC_MAX_PATH_LEN, "-%s_patch_mat_view", patch->classname));
3057   PetscCall(PetscOptionsGetViewer(comm, ((PetscObject)pc)->options, prefix, option, &patch->viewerMatrix, &patch->formatMatrix, &patch->viewMatrix));
3058   PetscOptionsHeadEnd();
3059   patch->optionsSet = PETSC_TRUE;
3060   PetscFunctionReturn(PETSC_SUCCESS);
3061 }
3062 
3063 static PetscErrorCode PCSetUpOnBlocks_PATCH(PC pc)
3064 {
3065   PC_PATCH          *patch = (PC_PATCH *)pc->data;
3066   KSPConvergedReason reason;
3067   PetscInt           i;
3068 
3069   PetscFunctionBegin;
3070   if (!patch->save_operators) {
3071     /* Can't do this here because the sub KSPs don't have an operator attached yet. */
3072     PetscFunctionReturn(PETSC_SUCCESS);
3073   }
3074   if (patch->denseinverse) {
3075     /* No solvers */
3076     PetscFunctionReturn(PETSC_SUCCESS);
3077   }
3078   for (i = 0; i < patch->npatch; ++i) {
3079     if (!((KSP)patch->solver[i])->setfromoptionscalled) PetscCall(KSPSetFromOptions((KSP)patch->solver[i]));
3080     PetscCall(KSPSetUp((KSP)patch->solver[i]));
3081     PetscCall(KSPGetConvergedReason((KSP)patch->solver[i], &reason));
3082     if (reason == KSP_DIVERGED_PC_FAILED) pc->failedreason = PC_SUBPC_ERROR;
3083   }
3084   PetscFunctionReturn(PETSC_SUCCESS);
3085 }
3086 
3087 static PetscErrorCode PCView_PATCH(PC pc, PetscViewer viewer)
3088 {
3089   PC_PATCH   *patch = (PC_PATCH *)pc->data;
3090   PetscViewer sviewer;
3091   PetscBool   isascii;
3092   PetscMPIInt rank;
3093 
3094   PetscFunctionBegin;
3095   /* TODO Redo tabbing with set tbas in new style */
3096   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &isascii));
3097   if (!isascii) PetscFunctionReturn(PETSC_SUCCESS);
3098   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)pc), &rank));
3099   PetscCall(PetscViewerASCIIPushTab(viewer));
3100   PetscCall(PetscViewerASCIIPrintf(viewer, "Subspace Correction preconditioner with %" PetscInt_FMT " patches\n", patch->npatch));
3101   if (patch->local_composition_type == PC_COMPOSITE_MULTIPLICATIVE) {
3102     PetscCall(PetscViewerASCIIPrintf(viewer, "Schwarz type: multiplicative\n"));
3103   } else {
3104     PetscCall(PetscViewerASCIIPrintf(viewer, "Schwarz type: additive\n"));
3105   }
3106   if (patch->partition_of_unity) PetscCall(PetscViewerASCIIPrintf(viewer, "Weighting by partition of unity\n"));
3107   else PetscCall(PetscViewerASCIIPrintf(viewer, "Not weighting by partition of unity\n"));
3108   if (patch->symmetrise_sweep) PetscCall(PetscViewerASCIIPrintf(viewer, "Symmetrising sweep (start->end, then end->start)\n"));
3109   else PetscCall(PetscViewerASCIIPrintf(viewer, "Not symmetrising sweep\n"));
3110   if (!patch->precomputeElementTensors) PetscCall(PetscViewerASCIIPrintf(viewer, "Not precomputing element tensors (overlapping cells rebuilt in every patch assembly)\n"));
3111   else PetscCall(PetscViewerASCIIPrintf(viewer, "Precomputing element tensors (each cell assembled only once)\n"));
3112   if (!patch->save_operators) PetscCall(PetscViewerASCIIPrintf(viewer, "Not saving patch operators (rebuilt every PCApply)\n"));
3113   else PetscCall(PetscViewerASCIIPrintf(viewer, "Saving patch operators (rebuilt every PCSetUp)\n"));
3114   if (patch->patchconstructop == PCPatchConstruct_Star) PetscCall(PetscViewerASCIIPrintf(viewer, "Patch construction operator: star\n"));
3115   else if (patch->patchconstructop == PCPatchConstruct_Vanka) PetscCall(PetscViewerASCIIPrintf(viewer, "Patch construction operator: Vanka\n"));
3116   else if (patch->patchconstructop == PCPatchConstruct_User) PetscCall(PetscViewerASCIIPrintf(viewer, "Patch construction operator: user-specified\n"));
3117   else PetscCall(PetscViewerASCIIPrintf(viewer, "Patch construction operator: unknown\n"));
3118 
3119   if (patch->denseinverse) {
3120     PetscCall(PetscViewerASCIIPrintf(viewer, "Explicitly forming dense inverse and applying patch solver via MatMult.\n"));
3121   } else {
3122     if (patch->isNonlinear) {
3123       PetscCall(PetscViewerASCIIPrintf(viewer, "SNES on patches (all same):\n"));
3124     } else {
3125       PetscCall(PetscViewerASCIIPrintf(viewer, "KSP on patches (all same):\n"));
3126     }
3127     if (patch->solver) {
3128       PetscCall(PetscViewerGetSubViewer(viewer, PETSC_COMM_SELF, &sviewer));
3129       if (rank == 0) {
3130         PetscCall(PetscViewerASCIIPushTab(sviewer));
3131         PetscCall(PetscObjectView(patch->solver[0], sviewer));
3132         PetscCall(PetscViewerASCIIPopTab(sviewer));
3133       }
3134       PetscCall(PetscViewerRestoreSubViewer(viewer, PETSC_COMM_SELF, &sviewer));
3135     } else {
3136       PetscCall(PetscViewerASCIIPushTab(viewer));
3137       PetscCall(PetscViewerASCIIPrintf(viewer, "Solver not yet set.\n"));
3138       PetscCall(PetscViewerASCIIPopTab(viewer));
3139     }
3140   }
3141   PetscCall(PetscViewerASCIIPopTab(viewer));
3142   PetscFunctionReturn(PETSC_SUCCESS);
3143 }
3144 
3145 /*MC
3146    PCPATCH - A `PC` object that encapsulates flexible definition of blocks for overlapping and non-overlapping
3147    small block additive preconditioners. Block definition is based on topology from
3148    a `DM` and equation numbering from a `PetscSection`.
3149 
3150    Options Database Keys:
3151 + -pc_patch_cells_view   - Views the process local cell numbers for each patch
3152 . -pc_patch_points_view  - Views the process local mesh point numbers for each patch
3153 . -pc_patch_g2l_view     - Views the map between global dofs and patch local dofs for each patch
3154 . -pc_patch_patches_view - Views the global dofs associated with each patch and its boundary
3155 - -pc_patch_sub_mat_view - Views the matrix associated with each patch
3156 
3157    Level: intermediate
3158 
3159 .seealso: [](ch_ksp), `PCType`, `PCCreate()`, `PCSetType()`, `PCASM`, `PCJACOBI`, `PCPBJACOBI`, `PCVPBJACOBI`, `SNESPATCH`
3160 M*/
3161 PETSC_EXTERN PetscErrorCode PCCreate_Patch(PC pc)
3162 {
3163   PC_PATCH *patch;
3164 
3165   PetscFunctionBegin;
3166   PetscCall(PetscCitationsRegister(PCPatchCitation, &PCPatchcite));
3167   PetscCall(PetscNew(&patch));
3168 
3169   if (patch->subspaces_to_exclude) PetscCall(PetscHSetIDestroy(&patch->subspaces_to_exclude));
3170   PetscCall(PetscHSetICreate(&patch->subspaces_to_exclude));
3171 
3172   patch->classname   = "pc";
3173   patch->isNonlinear = PETSC_FALSE;
3174 
3175   /* Set some defaults */
3176   patch->combined                 = PETSC_FALSE;
3177   patch->save_operators           = PETSC_TRUE;
3178   patch->local_composition_type   = PC_COMPOSITE_ADDITIVE;
3179   patch->precomputeElementTensors = PETSC_FALSE;
3180   patch->partition_of_unity       = PETSC_FALSE;
3181   patch->codim                    = -1;
3182   patch->dim                      = -1;
3183   patch->vankadim                 = -1;
3184   patch->ignoredim                = -1;
3185   patch->pardecomp_overlap        = 0;
3186   patch->patchconstructop         = PCPatchConstruct_Star;
3187   patch->symmetrise_sweep         = PETSC_FALSE;
3188   patch->npatch                   = 0;
3189   patch->userIS                   = NULL;
3190   patch->optionsSet               = PETSC_FALSE;
3191   patch->iterationSet             = NULL;
3192   patch->user_patches             = PETSC_FALSE;
3193   PetscCall(PetscStrallocpy(MATDENSE, (char **)&patch->sub_mat_type));
3194   patch->viewPatches                       = PETSC_FALSE;
3195   patch->viewCells                         = PETSC_FALSE;
3196   patch->viewPoints                        = PETSC_FALSE;
3197   patch->viewSection                       = PETSC_FALSE;
3198   patch->viewMatrix                        = PETSC_FALSE;
3199   patch->densesolve                        = NULL;
3200   patch->setupsolver                       = PCSetUp_PATCH_Linear;
3201   patch->applysolver                       = PCApply_PATCH_Linear;
3202   patch->resetsolver                       = PCReset_PATCH_Linear;
3203   patch->destroysolver                     = PCDestroy_PATCH_Linear;
3204   patch->updatemultiplicative              = PCUpdateMultiplicative_PATCH_Linear;
3205   patch->dofMappingWithoutToWithArtificial = NULL;
3206   patch->dofMappingWithoutToWithAll        = NULL;
3207 
3208   pc->data                 = (void *)patch;
3209   pc->ops->apply           = PCApply_PATCH;
3210   pc->ops->applytranspose  = NULL; /* PCApplyTranspose_PATCH; */
3211   pc->ops->setup           = PCSetUp_PATCH;
3212   pc->ops->reset           = PCReset_PATCH;
3213   pc->ops->destroy         = PCDestroy_PATCH;
3214   pc->ops->setfromoptions  = PCSetFromOptions_PATCH;
3215   pc->ops->setuponblocks   = PCSetUpOnBlocks_PATCH;
3216   pc->ops->view            = PCView_PATCH;
3217   pc->ops->applyrichardson = NULL;
3218 
3219   PetscFunctionReturn(PETSC_SUCCESS);
3220 }
3221