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