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