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