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