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