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