xref: /petsc/src/dm/impls/plex/plexsubmesh.c (revision 2ff79c18c26c94ed8cb599682f680f231dca6444)
1 #include <petsc/private/dmpleximpl.h>  /*I      "petscdmplex.h"    I*/
2 #include <petsc/private/dmlabelimpl.h> /*I      "petscdmlabel.h"   I*/
3 #include <petscsf.h>
4 
5 static PetscErrorCode DMPlexCellIsHybrid_Internal(DM dm, PetscInt p, PetscBool *isHybrid)
6 {
7   DMPolytopeType ct;
8 
9   PetscFunctionBegin;
10   PetscCall(DMPlexGetCellType(dm, p, &ct));
11   switch (ct) {
12   case DM_POLYTOPE_POINT_PRISM_TENSOR:
13   case DM_POLYTOPE_SEG_PRISM_TENSOR:
14   case DM_POLYTOPE_TRI_PRISM_TENSOR:
15   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
16     *isHybrid = PETSC_TRUE;
17     break;
18   default:
19     *isHybrid = PETSC_FALSE;
20     break;
21   }
22   PetscFunctionReturn(PETSC_SUCCESS);
23 }
24 
25 static PetscErrorCode DMPlexGetTensorPrismBounds_Internal(DM dm, PetscInt dim, PetscInt *cStart, PetscInt *cEnd)
26 {
27   DMLabel ctLabel;
28 
29   PetscFunctionBegin;
30   if (cStart) *cStart = -1;
31   if (cEnd) *cEnd = -1;
32   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
33   switch (dim) {
34   case 1:
35     PetscCall(DMLabelGetStratumBounds(ctLabel, DM_POLYTOPE_POINT_PRISM_TENSOR, cStart, cEnd));
36     break;
37   case 2:
38     PetscCall(DMLabelGetStratumBounds(ctLabel, DM_POLYTOPE_SEG_PRISM_TENSOR, cStart, cEnd));
39     break;
40   case 3:
41     PetscCall(DMLabelGetStratumBounds(ctLabel, DM_POLYTOPE_TRI_PRISM_TENSOR, cStart, cEnd));
42     if (*cStart < 0) PetscCall(DMLabelGetStratumBounds(ctLabel, DM_POLYTOPE_QUAD_PRISM_TENSOR, cStart, cEnd));
43     break;
44   default:
45     PetscFunctionReturn(PETSC_SUCCESS);
46   }
47   PetscFunctionReturn(PETSC_SUCCESS);
48 }
49 
50 PetscErrorCode DMPlexMarkBoundaryFaces_Internal(DM dm, PetscInt val, PetscInt cellHeight, DMLabel label, PetscBool missing_only)
51 {
52   PetscInt           depth, pStart, pEnd, fStart, fEnd, f, supportSize, nroots = -1, nleaves = -1, defval;
53   PetscSF            sf;
54   const PetscSFNode *iremote = NULL;
55   const PetscInt    *ilocal  = NULL;
56   PetscInt          *leafData;
57 
58   PetscFunctionBegin;
59   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
60   PetscCall(DMPlexGetDepth(dm, &depth));
61   if (depth >= cellHeight + 1) {
62     PetscCall(DMPlexGetHeightStratum(dm, cellHeight + 1, &fStart, &fEnd));
63   } else {
64     /* Note DMPlexGetHeightStratum() returns fStart, fEnd = pStart, pEnd */
65     /* if height > depth, which is not what we want here.                */
66     fStart = 0;
67     fEnd   = 0;
68   }
69   PetscCall(DMLabelGetDefaultValue(label, &defval));
70   PetscCall(PetscCalloc1(pEnd - pStart, &leafData));
71   leafData = PetscSafePointerPlusOffset(leafData, -pStart);
72   PetscCall(DMGetPointSF(dm, &sf));
73   if (sf) PetscCall(PetscSFGetGraph(sf, &nroots, &nleaves, &ilocal, &iremote));
74   if (sf && nroots >= 0) {
75     PetscInt        cStart, cEnd, c, i;
76     PetscInt       *rootData, *rootData1, *cellOwners, hasTwoSupportCells = -2;
77     const PetscInt *support;
78     PetscMPIInt     rank;
79 
80     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
81     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
82     PetscCall(PetscCalloc3(pEnd - pStart, &rootData, pEnd - pStart, &rootData1, cEnd - cStart, &cellOwners));
83     rootData -= pStart;
84     rootData1 -= pStart;
85     for (c = cStart; c < cEnd; ++c) cellOwners[c - cStart] = rank;
86     for (i = 0; i < nleaves; ++i) {
87       c = ilocal ? ilocal[i] : i;
88       if (c >= cStart && c < cEnd) cellOwners[c - cStart] = iremote[i].rank;
89     }
90     for (f = fStart; f < fEnd; ++f) {
91       PetscCall(DMPlexGetSupportSize(dm, f, &supportSize));
92       if (supportSize == 1) {
93         PetscCall(DMPlexGetSupport(dm, f, &support));
94         leafData[f] = cellOwners[support[0] - cStart];
95       } else {
96         /* TODO: When using DMForest, we could have a parent facet (a coarse facet)     */
97         /*       supportSize of which alone does not tell us if it is an interior       */
98         /*       facet or an exterior facet. Those facets can be identified by checking */
99         /*       if they are in the parent tree. We should probably skip those parent   */
100         /*       facets here, which will allow for including the following check, and   */
101         /*       see if they are exterior facets or not afterwards by checking if the   */
102         /*       children are exterior or not.                                          */
103         /* PetscCheck(supportSize == 2, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid facet support size (%" PetscInt_FMT ") on facet (%" PetscInt_FMT ")", supportSize, f); */
104         leafData[f] = hasTwoSupportCells; /* some negative PetscInt */
105       }
106       rootData[f]  = leafData[f];
107       rootData1[f] = leafData[f];
108     }
109     PetscCall(PetscSFReduceBegin(sf, MPIU_INT, leafData, rootData, MPI_MIN));
110     PetscCall(PetscSFReduceBegin(sf, MPIU_INT, leafData, rootData1, MPI_MAX));
111     PetscCall(PetscSFReduceEnd(sf, MPIU_INT, leafData, rootData, MPI_MIN));
112     PetscCall(PetscSFReduceEnd(sf, MPIU_INT, leafData, rootData1, MPI_MAX));
113     for (f = fStart; f < fEnd; ++f) {
114       /* Store global support size of f.                                                    */
115       /* Facet f is an interior facet if and only if one of the following two is satisfied: */
116       /* 1. supportSize is 2 on some rank.                                                  */
117       /* 2. supportSize is 1 on any rank that can see f, but f is on a partition boundary;  */
118       /*    i.e., rootData[f] < rootData1[f].                                               */
119       rootData[f] = (rootData[f] == hasTwoSupportCells || (rootData[f] < rootData1[f])) ? 2 : 1;
120       leafData[f] = rootData[f];
121     }
122     PetscCall(PetscSFBcastBegin(sf, MPIU_INT, rootData, leafData, MPI_REPLACE));
123     PetscCall(PetscSFBcastEnd(sf, MPIU_INT, rootData, leafData, MPI_REPLACE));
124     rootData += pStart;
125     rootData1 += pStart;
126     PetscCall(PetscFree3(rootData, rootData1, cellOwners));
127   } else {
128     for (f = fStart; f < fEnd; ++f) {
129       PetscCall(DMPlexGetSupportSize(dm, f, &supportSize));
130       leafData[f] = supportSize;
131     }
132   }
133   for (f = fStart; f < fEnd; ++f) {
134     if (leafData[f] == 1) {
135       if (val < 0) {
136         PetscInt *closure = NULL;
137         PetscInt  clSize, cl, cval;
138 
139         PetscAssert(!missing_only, PETSC_COMM_SELF, PETSC_ERR_SUP, "Not implemented");
140         PetscCall(DMPlexGetTransitiveClosure(dm, f, PETSC_TRUE, &clSize, &closure));
141         for (cl = 0; cl < clSize * 2; cl += 2) {
142           PetscCall(DMLabelGetValue(label, closure[cl], &cval));
143           if (cval < 0) continue;
144           PetscCall(DMLabelSetValue(label, f, cval));
145           break;
146         }
147         if (cl == clSize * 2) PetscCall(DMLabelSetValue(label, f, 1));
148         PetscCall(DMPlexRestoreTransitiveClosure(dm, f, PETSC_TRUE, &clSize, &closure));
149       } else {
150         if (missing_only) {
151           PetscInt fval;
152           PetscCall(DMLabelGetValue(label, f, &fval));
153           if (fval != defval) PetscCall(DMLabelClearValue(label, f, fval));
154           else PetscCall(DMLabelSetValue(label, f, val));
155         } else {
156           PetscCall(DMLabelSetValue(label, f, val));
157         }
158       }
159     } else {
160       /* TODO: See the above comment on DMForest */
161       /* PetscCheck(leafData[f] == 2, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid facet support size (%" PetscInt_FMT ") on facet (%" PetscInt_FMT ")", leafData[f], f); */
162     }
163   }
164   leafData = PetscSafePointerPlusOffset(leafData, pStart);
165   PetscCall(PetscFree(leafData));
166   PetscFunctionReturn(PETSC_SUCCESS);
167 }
168 
169 /*@
170   DMPlexMarkBoundaryFaces - Mark all faces on the boundary
171 
172   Collective
173 
174   Input Parameters:
175 + dm  - The original `DM`
176 - val - The marker value, or `PETSC_DETERMINE` to use some value in the closure (or 1 if none are found)
177 
178   Output Parameter:
179 . label - The `DMLabel` marking boundary faces with the given value
180 
181   Level: developer
182 
183   Note:
184   This function will use the point `PetscSF` from the input `DM` and the ownership of the support cells to exclude points on the partition boundary from being marked. If you also wish to mark the partition boundary, you can use `DMSetPointSF()` to temporarily set it to `NULL`, and then reset it to the original object after the call.
185 
186   In DMForest there can be facets support sizes of which alone can not determine whether they are on the boundary. Currently, this function is not guaranteed to produce the correct result in such case.
187 
188 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLabelCreate()`, `DMCreateLabel()`
189 @*/
190 PetscErrorCode DMPlexMarkBoundaryFaces(DM dm, PetscInt val, DMLabel label)
191 {
192   DM                     plex;
193   DMPlexInterpolatedFlag flg;
194 
195   PetscFunctionBegin;
196   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
197   PetscCall(DMConvert(dm, DMPLEX, &plex));
198   PetscCall(DMPlexIsInterpolated(plex, &flg));
199   PetscCheck(flg == DMPLEX_INTERPOLATED_FULL, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "DM is not fully interpolated on this rank");
200   PetscCall(DMPlexMarkBoundaryFaces_Internal(plex, val, 0, label, PETSC_FALSE));
201   PetscCall(DMDestroy(&plex));
202   PetscFunctionReturn(PETSC_SUCCESS);
203 }
204 
205 static PetscErrorCode DMPlexLabelComplete_Internal(DM dm, DMLabel label, PetscBool completeCells)
206 {
207   IS              valueIS;
208   PetscSF         sfPoint;
209   const PetscInt *values;
210   PetscInt        numValues, v, cStart, cEnd, nroots;
211 
212   PetscFunctionBegin;
213   PetscCall(DMLabelGetNumValues(label, &numValues));
214   PetscCall(DMLabelGetValueIS(label, &valueIS));
215   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
216   PetscCall(ISGetIndices(valueIS, &values));
217   for (v = 0; v < numValues; ++v) {
218     IS              pointIS;
219     const PetscInt *points;
220     PetscInt        numPoints, p;
221 
222     PetscCall(DMLabelGetStratumSize(label, values[v], &numPoints));
223     PetscCall(DMLabelGetStratumIS(label, values[v], &pointIS));
224     PetscCall(ISGetIndices(pointIS, &points));
225     for (p = 0; p < numPoints; ++p) {
226       PetscInt  q       = points[p];
227       PetscInt *closure = NULL;
228       PetscInt  closureSize, c;
229 
230       if (cStart <= q && q < cEnd && !completeCells) { /* skip cells */
231         continue;
232       }
233       PetscCall(DMPlexGetTransitiveClosure(dm, q, PETSC_TRUE, &closureSize, &closure));
234       for (c = 0; c < closureSize * 2; c += 2) PetscCall(DMLabelSetValue(label, closure[c], values[v]));
235       PetscCall(DMPlexRestoreTransitiveClosure(dm, q, PETSC_TRUE, &closureSize, &closure));
236     }
237     PetscCall(ISRestoreIndices(pointIS, &points));
238     PetscCall(ISDestroy(&pointIS));
239   }
240   PetscCall(ISRestoreIndices(valueIS, &values));
241   PetscCall(ISDestroy(&valueIS));
242   PetscCall(DMGetPointSF(dm, &sfPoint));
243   PetscCall(PetscSFGetGraph(sfPoint, &nroots, NULL, NULL, NULL));
244   if (nroots >= 0) {
245     DMLabel         lblRoots, lblLeaves;
246     IS              valueIS, pointIS;
247     const PetscInt *values;
248     PetscInt        numValues, v;
249 
250     /* Pull point contributions from remote leaves into local roots */
251     PetscCall(DMLabelGather(label, sfPoint, &lblLeaves));
252     PetscCall(DMLabelGetValueIS(lblLeaves, &valueIS));
253     PetscCall(ISGetLocalSize(valueIS, &numValues));
254     PetscCall(ISGetIndices(valueIS, &values));
255     for (v = 0; v < numValues; ++v) {
256       const PetscInt value = values[v];
257 
258       PetscCall(DMLabelGetStratumIS(lblLeaves, value, &pointIS));
259       PetscCall(DMLabelInsertIS(label, pointIS, value));
260       PetscCall(ISDestroy(&pointIS));
261     }
262     PetscCall(ISRestoreIndices(valueIS, &values));
263     PetscCall(ISDestroy(&valueIS));
264     PetscCall(DMLabelDestroy(&lblLeaves));
265     /* Push point contributions from roots into remote leaves */
266     PetscCall(DMLabelDistribute(label, sfPoint, &lblRoots));
267     PetscCall(DMLabelGetValueIS(lblRoots, &valueIS));
268     PetscCall(ISGetLocalSize(valueIS, &numValues));
269     PetscCall(ISGetIndices(valueIS, &values));
270     for (v = 0; v < numValues; ++v) {
271       const PetscInt value = values[v];
272 
273       PetscCall(DMLabelGetStratumIS(lblRoots, value, &pointIS));
274       PetscCall(DMLabelInsertIS(label, pointIS, value));
275       PetscCall(ISDestroy(&pointIS));
276     }
277     PetscCall(ISRestoreIndices(valueIS, &values));
278     PetscCall(ISDestroy(&valueIS));
279     PetscCall(DMLabelDestroy(&lblRoots));
280   }
281   PetscFunctionReturn(PETSC_SUCCESS);
282 }
283 
284 /*@
285   DMPlexLabelComplete - Starting with a label marking points on a surface, we add the transitive closure to the surface
286 
287   Input Parameters:
288 + dm    - The `DM`
289 - label - A `DMLabel` marking the surface points
290 
291   Output Parameter:
292 . label - A `DMLabel` marking all surface points in the transitive closure
293 
294   Level: developer
295 
296 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexLabelCohesiveComplete()`
297 @*/
298 PetscErrorCode DMPlexLabelComplete(DM dm, DMLabel label)
299 {
300   PetscFunctionBegin;
301   PetscCall(DMPlexLabelComplete_Internal(dm, label, PETSC_TRUE));
302   PetscFunctionReturn(PETSC_SUCCESS);
303 }
304 
305 /*@
306   DMPlexLabelAddCells - Starting with a label marking points on a surface, we add a cell for each point
307 
308   Input Parameters:
309 + dm    - The `DM`
310 - label - A `DMLabel` marking the surface points
311 
312   Output Parameter:
313 . label - A `DMLabel` incorporating cells
314 
315   Level: developer
316 
317   Note:
318   The cells allow FEM boundary conditions to be applied using the cell geometry
319 
320 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexLabelAddFaceCells()`, `DMPlexLabelComplete()`, `DMPlexLabelCohesiveComplete()`
321 @*/
322 PetscErrorCode DMPlexLabelAddCells(DM dm, DMLabel label)
323 {
324   IS              valueIS;
325   const PetscInt *values;
326   PetscInt        numValues, v, csStart, csEnd, chStart, chEnd;
327 
328   PetscFunctionBegin;
329   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &csStart, &csEnd));
330   PetscCall(DMPlexGetHeightStratum(dm, 0, &chStart, &chEnd));
331   PetscCall(DMLabelGetNumValues(label, &numValues));
332   PetscCall(DMLabelGetValueIS(label, &valueIS));
333   PetscCall(ISGetIndices(valueIS, &values));
334   for (v = 0; v < numValues; ++v) {
335     IS              pointIS;
336     const PetscInt *points;
337     PetscInt        numPoints, p;
338 
339     PetscCall(DMLabelGetStratumSize(label, values[v], &numPoints));
340     PetscCall(DMLabelGetStratumIS(label, values[v], &pointIS));
341     PetscCall(ISGetIndices(pointIS, &points));
342     for (p = 0; p < numPoints; ++p) {
343       const PetscInt point   = points[p];
344       PetscInt      *closure = NULL;
345       PetscInt       closureSize, cl, h, cStart, cEnd;
346       DMPolytopeType ct;
347 
348       // If the point is a hybrid, allow hybrid cells
349       PetscCall(DMPlexGetCellType(dm, point, &ct));
350       PetscCall(DMPlexGetPointHeight(dm, point, &h));
351       if (DMPolytopeTypeIsHybrid(ct)) {
352         cStart = chStart;
353         cEnd   = chEnd;
354       } else {
355         cStart = csStart;
356         cEnd   = csEnd;
357       }
358 
359       PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closure));
360       for (cl = closureSize - 1; cl > 0; --cl) {
361         const PetscInt cell = closure[cl * 2];
362         if (cell >= cStart && cell < cEnd) {
363           PetscCall(DMLabelSetValue(label, cell, values[v]));
364           break;
365         }
366       }
367       PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closure));
368     }
369     PetscCall(ISRestoreIndices(pointIS, &points));
370     PetscCall(ISDestroy(&pointIS));
371   }
372   PetscCall(ISRestoreIndices(valueIS, &values));
373   PetscCall(ISDestroy(&valueIS));
374   PetscFunctionReturn(PETSC_SUCCESS);
375 }
376 
377 /*@
378   DMPlexLabelAddFaceCells - Starting with a label marking faces on a surface, we add a cell for each face
379 
380   Input Parameters:
381 + dm    - The `DM`
382 - label - A `DMLabel` marking the surface points
383 
384   Output Parameter:
385 . label - A `DMLabel` incorporating cells
386 
387   Level: developer
388 
389   Note:
390   The cells allow FEM boundary conditions to be applied using the cell geometry
391 
392 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexLabelAddCells()`, `DMPlexLabelComplete()`, `DMPlexLabelCohesiveComplete()`
393 @*/
394 PetscErrorCode DMPlexLabelAddFaceCells(DM dm, DMLabel label)
395 {
396   IS              valueIS;
397   const PetscInt *values;
398   PetscInt        numValues, v, cStart, cEnd, fStart, fEnd;
399 
400   PetscFunctionBegin;
401   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
402   PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
403   PetscCall(DMLabelGetNumValues(label, &numValues));
404   PetscCall(DMLabelGetValueIS(label, &valueIS));
405   PetscCall(ISGetIndices(valueIS, &values));
406   for (v = 0; v < numValues; ++v) {
407     IS              pointIS;
408     const PetscInt *points;
409     PetscInt        numPoints, p;
410 
411     PetscCall(DMLabelGetStratumSize(label, values[v], &numPoints));
412     PetscCall(DMLabelGetStratumIS(label, values[v], &pointIS));
413     PetscCall(ISGetIndices(pointIS, &points));
414     for (p = 0; p < numPoints; ++p) {
415       const PetscInt face    = points[p];
416       PetscInt      *closure = NULL;
417       PetscInt       closureSize, cl;
418 
419       if ((face < fStart) || (face >= fEnd)) continue;
420       PetscCall(DMPlexGetTransitiveClosure(dm, face, PETSC_FALSE, &closureSize, &closure));
421       for (cl = closureSize - 1; cl > 0; --cl) {
422         const PetscInt cell = closure[cl * 2];
423         if (cell >= cStart && cell < cEnd) {
424           PetscCall(DMLabelSetValue(label, cell, values[v]));
425           break;
426         }
427       }
428       PetscCall(DMPlexRestoreTransitiveClosure(dm, face, PETSC_FALSE, &closureSize, &closure));
429     }
430     PetscCall(ISRestoreIndices(pointIS, &points));
431     PetscCall(ISDestroy(&pointIS));
432   }
433   PetscCall(ISRestoreIndices(valueIS, &values));
434   PetscCall(ISDestroy(&valueIS));
435   PetscFunctionReturn(PETSC_SUCCESS);
436 }
437 
438 /*@
439   DMPlexLabelClearCells - Remove cells from a label
440 
441   Input Parameters:
442 + dm    - The `DM`
443 - label - A `DMLabel` marking surface points and their adjacent cells
444 
445   Output Parameter:
446 . label - A `DMLabel` without cells
447 
448   Level: developer
449 
450   Note:
451   This undoes `DMPlexLabelAddCells()` or `DMPlexLabelAddFaceCells()`
452 
453 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexLabelComplete()`, `DMPlexLabelCohesiveComplete()`, `DMPlexLabelAddCells()`
454 @*/
455 PetscErrorCode DMPlexLabelClearCells(DM dm, DMLabel label)
456 {
457   IS              valueIS;
458   const PetscInt *values;
459   PetscInt        numValues, v, cStart, cEnd;
460 
461   PetscFunctionBegin;
462   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
463   PetscCall(DMLabelGetNumValues(label, &numValues));
464   PetscCall(DMLabelGetValueIS(label, &valueIS));
465   PetscCall(ISGetIndices(valueIS, &values));
466   for (v = 0; v < numValues; ++v) {
467     IS              pointIS;
468     const PetscInt *points;
469     PetscInt        numPoints, p;
470 
471     PetscCall(DMLabelGetStratumSize(label, values[v], &numPoints));
472     PetscCall(DMLabelGetStratumIS(label, values[v], &pointIS));
473     PetscCall(ISGetIndices(pointIS, &points));
474     for (p = 0; p < numPoints; ++p) {
475       PetscInt point = points[p];
476 
477       if (point >= cStart && point < cEnd) PetscCall(DMLabelClearValue(label, point, values[v]));
478     }
479     PetscCall(ISRestoreIndices(pointIS, &points));
480     PetscCall(ISDestroy(&pointIS));
481   }
482   PetscCall(ISRestoreIndices(valueIS, &values));
483   PetscCall(ISDestroy(&valueIS));
484   PetscFunctionReturn(PETSC_SUCCESS);
485 }
486 
487 /* take (oldEnd, added) pairs, ordered by height and convert them to (oldstart, newstart) pairs, ordered by ascending
488  * index (skipping first, which is (0,0)) */
489 static inline PetscErrorCode DMPlexShiftPointSetUp_Internal(PetscInt depth, PetscInt depthShift[])
490 {
491   PetscInt d, off = 0;
492 
493   PetscFunctionBegin;
494   /* sort by (oldend): yes this is an O(n^2) sort, we expect depth <= 3 */
495   for (d = 0; d < depth; d++) {
496     PetscInt firstd     = d;
497     PetscInt firstStart = depthShift[2 * d];
498     PetscInt e;
499 
500     for (e = d + 1; e <= depth; e++) {
501       if (depthShift[2 * e] < firstStart) {
502         firstd     = e;
503         firstStart = depthShift[2 * d];
504       }
505     }
506     if (firstd != d) {
507       PetscInt swap[2];
508 
509       e                     = firstd;
510       swap[0]               = depthShift[2 * d];
511       swap[1]               = depthShift[2 * d + 1];
512       depthShift[2 * d]     = depthShift[2 * e];
513       depthShift[2 * d + 1] = depthShift[2 * e + 1];
514       depthShift[2 * e]     = swap[0];
515       depthShift[2 * e + 1] = swap[1];
516     }
517   }
518   /* convert (oldstart, added) to (oldstart, newstart) */
519   for (d = 0; d <= depth; d++) {
520     off += depthShift[2 * d + 1];
521     depthShift[2 * d + 1] = depthShift[2 * d] + off;
522   }
523   PetscFunctionReturn(PETSC_SUCCESS);
524 }
525 
526 /* depthShift is a list of (old, new) pairs */
527 static inline PetscInt DMPlexShiftPoint_Internal(PetscInt p, PetscInt depth, PetscInt depthShift[])
528 {
529   PetscInt d;
530   PetscInt newOff = 0;
531 
532   for (d = 0; d <= depth; d++) {
533     if (p < depthShift[2 * d]) return p + newOff;
534     else newOff = depthShift[2 * d + 1] - depthShift[2 * d];
535   }
536   return p + newOff;
537 }
538 
539 /* depthShift is a list of (old, new) pairs */
540 static inline PetscInt DMPlexShiftPointInverse_Internal(PetscInt p, PetscInt depth, PetscInt depthShift[])
541 {
542   PetscInt d;
543   PetscInt newOff = 0;
544 
545   for (d = 0; d <= depth; d++) {
546     if (p < depthShift[2 * d + 1]) return p + newOff;
547     else newOff = depthShift[2 * d] - depthShift[2 * d + 1];
548   }
549   return p + newOff;
550 }
551 
552 static PetscErrorCode DMPlexShiftSizes_Internal(DM dm, PetscInt depthShift[], DM dmNew)
553 {
554   PetscInt depth = 0, d, pStart, pEnd, p;
555   DMLabel  depthLabel;
556 
557   PetscFunctionBegin;
558   PetscCall(DMPlexGetDepth(dm, &depth));
559   if (depth < 0) PetscFunctionReturn(PETSC_SUCCESS);
560   /* Step 1: Expand chart */
561   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
562   pEnd = DMPlexShiftPoint_Internal(pEnd, depth, depthShift);
563   PetscCall(DMPlexSetChart(dmNew, pStart, pEnd));
564   PetscCall(DMCreateLabel(dmNew, "depth"));
565   PetscCall(DMPlexGetDepthLabel(dmNew, &depthLabel));
566   PetscCall(DMCreateLabel(dmNew, "celltype"));
567   /* Step 2: Set cone and support sizes */
568   for (d = 0; d <= depth; ++d) {
569     PetscInt pStartNew, pEndNew;
570     IS       pIS;
571 
572     PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
573     pStartNew = DMPlexShiftPoint_Internal(pStart, depth, depthShift);
574     pEndNew   = DMPlexShiftPoint_Internal(pEnd, depth, depthShift);
575     PetscCall(ISCreateStride(PETSC_COMM_SELF, pEndNew - pStartNew, pStartNew, 1, &pIS));
576     PetscCall(DMLabelSetStratumIS(depthLabel, d, pIS));
577     PetscCall(ISDestroy(&pIS));
578     for (p = pStart; p < pEnd; ++p) {
579       PetscInt       newp = DMPlexShiftPoint_Internal(p, depth, depthShift);
580       PetscInt       size;
581       DMPolytopeType ct;
582 
583       PetscCall(DMPlexGetConeSize(dm, p, &size));
584       PetscCall(DMPlexSetConeSize(dmNew, newp, size));
585       PetscCall(DMPlexGetSupportSize(dm, p, &size));
586       PetscCall(DMPlexSetSupportSize(dmNew, newp, size));
587       PetscCall(DMPlexGetCellType(dm, p, &ct));
588       PetscCall(DMPlexSetCellType(dmNew, newp, ct));
589     }
590   }
591   PetscFunctionReturn(PETSC_SUCCESS);
592 }
593 
594 static PetscErrorCode DMPlexShiftPoints_Internal(DM dm, PetscInt depthShift[], DM dmNew)
595 {
596   PetscInt *newpoints;
597   PetscInt  depth = 0, maxConeSize, maxSupportSize, maxConeSizeNew, maxSupportSizeNew, pStart, pEnd, p;
598 
599   PetscFunctionBegin;
600   PetscCall(DMPlexGetDepth(dm, &depth));
601   if (depth < 0) PetscFunctionReturn(PETSC_SUCCESS);
602   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
603   PetscCall(DMPlexGetMaxSizes(dmNew, &maxConeSizeNew, &maxSupportSizeNew));
604   PetscCall(PetscMalloc1(PetscMax(PetscMax(maxConeSize, maxSupportSize), PetscMax(maxConeSizeNew, maxSupportSizeNew)), &newpoints));
605   /* Step 5: Set cones and supports */
606   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
607   for (p = pStart; p < pEnd; ++p) {
608     const PetscInt *points = NULL, *orientations = NULL;
609     PetscInt        size, sizeNew, i, newp = DMPlexShiftPoint_Internal(p, depth, depthShift);
610 
611     PetscCall(DMPlexGetConeSize(dm, p, &size));
612     PetscCall(DMPlexGetCone(dm, p, &points));
613     PetscCall(DMPlexGetConeOrientation(dm, p, &orientations));
614     for (i = 0; i < size; ++i) newpoints[i] = DMPlexShiftPoint_Internal(points[i], depth, depthShift);
615     PetscCall(DMPlexSetCone(dmNew, newp, newpoints));
616     PetscCall(DMPlexSetConeOrientation(dmNew, newp, orientations));
617     PetscCall(DMPlexGetSupportSize(dm, p, &size));
618     PetscCall(DMPlexGetSupportSize(dmNew, newp, &sizeNew));
619     PetscCall(DMPlexGetSupport(dm, p, &points));
620     for (i = 0; i < size; ++i) newpoints[i] = DMPlexShiftPoint_Internal(points[i], depth, depthShift);
621     for (i = size; i < sizeNew; ++i) newpoints[i] = 0;
622     PetscCall(DMPlexSetSupport(dmNew, newp, newpoints));
623   }
624   PetscCall(PetscFree(newpoints));
625   PetscFunctionReturn(PETSC_SUCCESS);
626 }
627 
628 static PetscErrorCode DMPlexShiftCoordinates_Internal(DM dm, PetscInt depthShift[], DM dmNew)
629 {
630   PetscSection coordSection, newCoordSection;
631   Vec          coordinates, newCoordinates;
632   PetscScalar *coords, *newCoords;
633   PetscInt     coordSize, sStart, sEnd;
634   PetscInt     dim, depth = 0, cStart, cEnd, cStartNew, cEndNew, c, vStart, vEnd, vStartNew, vEndNew, v;
635   PetscBool    hasCells;
636 
637   PetscFunctionBegin;
638   PetscCall(DMGetCoordinateDim(dm, &dim));
639   PetscCall(DMSetCoordinateDim(dmNew, dim));
640   PetscCall(DMPlexGetDepth(dm, &depth));
641   /* Step 8: Convert coordinates */
642   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
643   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
644   PetscCall(DMPlexGetDepthStratum(dmNew, 0, &vStartNew, &vEndNew));
645   PetscCall(DMPlexGetHeightStratum(dmNew, 0, &cStartNew, &cEndNew));
646   PetscCall(DMGetCoordinateSection(dm, &coordSection));
647   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &newCoordSection));
648   PetscCall(PetscSectionSetNumFields(newCoordSection, 1));
649   PetscCall(PetscSectionSetFieldComponents(newCoordSection, 0, dim));
650   PetscCall(PetscSectionGetChart(coordSection, &sStart, &sEnd));
651   hasCells = sStart == cStart ? PETSC_TRUE : PETSC_FALSE;
652   PetscCall(PetscSectionSetChart(newCoordSection, hasCells ? cStartNew : vStartNew, vEndNew));
653   if (hasCells) {
654     for (c = cStart; c < cEnd; ++c) {
655       PetscInt cNew = DMPlexShiftPoint_Internal(c, depth, depthShift), dof;
656 
657       PetscCall(PetscSectionGetDof(coordSection, c, &dof));
658       PetscCall(PetscSectionSetDof(newCoordSection, cNew, dof));
659       PetscCall(PetscSectionSetFieldDof(newCoordSection, cNew, 0, dof));
660     }
661   }
662   for (v = vStartNew; v < vEndNew; ++v) {
663     PetscCall(PetscSectionSetDof(newCoordSection, v, dim));
664     PetscCall(PetscSectionSetFieldDof(newCoordSection, v, 0, dim));
665   }
666   PetscCall(PetscSectionSetUp(newCoordSection));
667   PetscCall(DMSetCoordinateSection(dmNew, PETSC_DETERMINE, newCoordSection));
668   PetscCall(PetscSectionGetStorageSize(newCoordSection, &coordSize));
669   PetscCall(VecCreate(PETSC_COMM_SELF, &newCoordinates));
670   PetscCall(PetscObjectSetName((PetscObject)newCoordinates, "coordinates"));
671   PetscCall(VecSetSizes(newCoordinates, coordSize, PETSC_DETERMINE));
672   PetscCall(VecSetBlockSize(newCoordinates, dim));
673   PetscCall(VecSetType(newCoordinates, VECSTANDARD));
674   PetscCall(DMSetCoordinatesLocal(dmNew, newCoordinates));
675   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
676   PetscCall(VecGetArray(coordinates, &coords));
677   PetscCall(VecGetArray(newCoordinates, &newCoords));
678   if (hasCells) {
679     for (c = cStart; c < cEnd; ++c) {
680       PetscInt cNew = DMPlexShiftPoint_Internal(c, depth, depthShift), dof, off, noff, d;
681 
682       PetscCall(PetscSectionGetDof(coordSection, c, &dof));
683       PetscCall(PetscSectionGetOffset(coordSection, c, &off));
684       PetscCall(PetscSectionGetOffset(newCoordSection, cNew, &noff));
685       for (d = 0; d < dof; ++d) newCoords[noff + d] = coords[off + d];
686     }
687   }
688   for (v = vStart; v < vEnd; ++v) {
689     PetscInt dof, off, noff, d;
690 
691     PetscCall(PetscSectionGetDof(coordSection, v, &dof));
692     PetscCall(PetscSectionGetOffset(coordSection, v, &off));
693     PetscCall(PetscSectionGetOffset(newCoordSection, DMPlexShiftPoint_Internal(v, depth, depthShift), &noff));
694     for (d = 0; d < dof; ++d) newCoords[noff + d] = coords[off + d];
695   }
696   PetscCall(VecRestoreArray(coordinates, &coords));
697   PetscCall(VecRestoreArray(newCoordinates, &newCoords));
698   PetscCall(VecDestroy(&newCoordinates));
699   PetscCall(PetscSectionDestroy(&newCoordSection));
700   PetscFunctionReturn(PETSC_SUCCESS);
701 }
702 
703 static PetscErrorCode DMPlexShiftSF_Single(DM dm, PetscInt depthShift[], PetscSF sf, PetscSF sfNew)
704 {
705   const PetscSFNode *remotePoints;
706   PetscSFNode       *gremotePoints;
707   const PetscInt    *localPoints;
708   PetscInt          *glocalPoints, *newLocation, *newRemoteLocation;
709   PetscInt           numRoots, numLeaves, l, pStart, pEnd, depth = 0, totShift = 0;
710 
711   PetscFunctionBegin;
712   PetscCall(DMPlexGetDepth(dm, &depth));
713   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
714   PetscCall(PetscSFGetGraph(sf, &numRoots, &numLeaves, &localPoints, &remotePoints));
715   totShift = DMPlexShiftPoint_Internal(pEnd, depth, depthShift) - pEnd;
716   if (numRoots >= 0) {
717     PetscCall(PetscMalloc2(numRoots, &newLocation, pEnd - pStart, &newRemoteLocation));
718     for (l = 0; l < numRoots; ++l) newLocation[l] = DMPlexShiftPoint_Internal(l, depth, depthShift);
719     PetscCall(PetscSFBcastBegin(sf, MPIU_INT, newLocation, newRemoteLocation, MPI_REPLACE));
720     PetscCall(PetscSFBcastEnd(sf, MPIU_INT, newLocation, newRemoteLocation, MPI_REPLACE));
721     PetscCall(PetscMalloc1(numLeaves, &glocalPoints));
722     PetscCall(PetscMalloc1(numLeaves, &gremotePoints));
723     for (l = 0; l < numLeaves; ++l) {
724       glocalPoints[l]        = DMPlexShiftPoint_Internal(localPoints[l], depth, depthShift);
725       gremotePoints[l].rank  = remotePoints[l].rank;
726       gremotePoints[l].index = newRemoteLocation[localPoints[l]];
727     }
728     PetscCall(PetscFree2(newLocation, newRemoteLocation));
729     PetscCall(PetscSFSetGraph(sfNew, numRoots + totShift, numLeaves, glocalPoints, PETSC_OWN_POINTER, gremotePoints, PETSC_OWN_POINTER));
730   }
731   PetscFunctionReturn(PETSC_SUCCESS);
732 }
733 
734 static PetscErrorCode DMPlexShiftSF_Internal(DM dm, PetscInt depthShift[], DM dmNew)
735 {
736   PetscSF   sfPoint, sfPointNew;
737   PetscBool useNatural;
738 
739   PetscFunctionBegin;
740   /* Step 9: Convert pointSF */
741   PetscCall(DMGetPointSF(dm, &sfPoint));
742   PetscCall(DMGetPointSF(dmNew, &sfPointNew));
743   PetscCall(DMPlexShiftSF_Single(dm, depthShift, sfPoint, sfPointNew));
744   /* Step 9b: Convert naturalSF */
745   PetscCall(DMGetUseNatural(dm, &useNatural));
746   if (useNatural) {
747     PetscSF sfNat, sfNatNew;
748 
749     PetscCall(DMSetUseNatural(dmNew, useNatural));
750     PetscCall(DMGetNaturalSF(dm, &sfNat));
751     PetscCall(DMGetNaturalSF(dmNew, &sfNatNew));
752     PetscCall(DMPlexShiftSF_Single(dm, depthShift, sfNat, sfNatNew));
753   }
754   PetscFunctionReturn(PETSC_SUCCESS);
755 }
756 
757 static PetscErrorCode DMPlexShiftLabels_Internal(DM dm, PetscInt depthShift[], DM dmNew)
758 {
759   PetscInt depth = 0, numLabels, l;
760 
761   PetscFunctionBegin;
762   PetscCall(DMPlexGetDepth(dm, &depth));
763   /* Step 10: Convert labels */
764   PetscCall(DMGetNumLabels(dm, &numLabels));
765   for (l = 0; l < numLabels; ++l) {
766     DMLabel         label, newlabel;
767     const char     *lname;
768     PetscBool       isDepth, isDim;
769     IS              valueIS;
770     const PetscInt *values;
771     PetscInt        numValues, val;
772 
773     PetscCall(DMGetLabelName(dm, l, &lname));
774     PetscCall(PetscStrcmp(lname, "depth", &isDepth));
775     if (isDepth) continue;
776     PetscCall(PetscStrcmp(lname, "dim", &isDim));
777     if (isDim) continue;
778     PetscCall(DMCreateLabel(dmNew, lname));
779     PetscCall(DMGetLabel(dm, lname, &label));
780     PetscCall(DMGetLabel(dmNew, lname, &newlabel));
781     PetscCall(DMLabelGetDefaultValue(label, &val));
782     PetscCall(DMLabelSetDefaultValue(newlabel, val));
783     PetscCall(DMLabelGetValueIS(label, &valueIS));
784     PetscCall(ISGetLocalSize(valueIS, &numValues));
785     PetscCall(ISGetIndices(valueIS, &values));
786     for (val = 0; val < numValues; ++val) {
787       IS              pointIS;
788       const PetscInt *points;
789       PetscInt        numPoints, p;
790 
791       PetscCall(DMLabelGetStratumIS(label, values[val], &pointIS));
792       PetscCall(ISGetLocalSize(pointIS, &numPoints));
793       PetscCall(ISGetIndices(pointIS, &points));
794       for (p = 0; p < numPoints; ++p) {
795         const PetscInt newpoint = DMPlexShiftPoint_Internal(points[p], depth, depthShift);
796 
797         PetscCall(DMLabelSetValue(newlabel, newpoint, values[val]));
798       }
799       PetscCall(ISRestoreIndices(pointIS, &points));
800       PetscCall(ISDestroy(&pointIS));
801     }
802     PetscCall(ISRestoreIndices(valueIS, &values));
803     PetscCall(ISDestroy(&valueIS));
804   }
805   PetscFunctionReturn(PETSC_SUCCESS);
806 }
807 
808 static PetscErrorCode DMPlexCreateVTKLabel_Internal(DM dm, PetscBool createGhostLabel, DM dmNew)
809 {
810   PetscSF            sfPoint;
811   DMLabel            vtkLabel, ghostLabel = NULL;
812   const PetscSFNode *leafRemote;
813   const PetscInt    *leafLocal;
814   PetscInt           cellHeight, cStart, cEnd, c, fStart, fEnd, f, numLeaves, l;
815   PetscMPIInt        rank;
816 
817   PetscFunctionBegin;
818   /* Step 11: Make label for output (vtk) and to mark ghost points (ghost) */
819   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
820   PetscCall(DMGetPointSF(dm, &sfPoint));
821   PetscCall(DMPlexGetVTKCellHeight(dmNew, &cellHeight));
822   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
823   PetscCall(PetscSFGetGraph(sfPoint, NULL, &numLeaves, &leafLocal, &leafRemote));
824   PetscCall(DMCreateLabel(dmNew, "vtk"));
825   PetscCall(DMGetLabel(dmNew, "vtk", &vtkLabel));
826   if (createGhostLabel) {
827     PetscCall(DMCreateLabel(dmNew, "ghost"));
828     PetscCall(DMGetLabel(dmNew, "ghost", &ghostLabel));
829   }
830   for (l = 0, c = cStart; l < numLeaves && c < cEnd; ++l, ++c) {
831     for (; c < leafLocal[l] && c < cEnd; ++c) PetscCall(DMLabelSetValue(vtkLabel, c, 1));
832     if (leafLocal[l] >= cEnd) break;
833     if (leafRemote[l].rank == rank) PetscCall(DMLabelSetValue(vtkLabel, c, 1));
834     else if (ghostLabel) PetscCall(DMLabelSetValue(ghostLabel, c, 2));
835   }
836   for (; c < cEnd; ++c) PetscCall(DMLabelSetValue(vtkLabel, c, 1));
837   if (ghostLabel) {
838     PetscCall(DMPlexGetHeightStratum(dmNew, 1, &fStart, &fEnd));
839     for (f = fStart; f < fEnd; ++f) {
840       PetscInt numCells;
841 
842       PetscCall(DMPlexGetSupportSize(dmNew, f, &numCells));
843       if (numCells < 2) {
844         PetscCall(DMLabelSetValue(ghostLabel, f, 1));
845       } else {
846         const PetscInt *cells = NULL;
847         PetscInt        vA, vB;
848 
849         PetscCall(DMPlexGetSupport(dmNew, f, &cells));
850         PetscCall(DMLabelGetValue(vtkLabel, cells[0], &vA));
851         PetscCall(DMLabelGetValue(vtkLabel, cells[1], &vB));
852         if (vA != 1 && vB != 1) PetscCall(DMLabelSetValue(ghostLabel, f, 1));
853       }
854     }
855   }
856   PetscFunctionReturn(PETSC_SUCCESS);
857 }
858 
859 static PetscErrorCode DMPlexShiftTree_Internal(DM dm, PetscInt depthShift[], DM dmNew)
860 {
861   DM           refTree;
862   PetscSection pSec;
863   PetscInt    *parents, *childIDs;
864 
865   PetscFunctionBegin;
866   PetscCall(DMPlexGetReferenceTree(dm, &refTree));
867   PetscCall(DMPlexSetReferenceTree(dmNew, refTree));
868   PetscCall(DMPlexGetTree(dm, &pSec, &parents, &childIDs, NULL, NULL));
869   if (pSec) {
870     PetscInt     p, pStart, pEnd, *parentsShifted, pStartShifted, pEndShifted, depth;
871     PetscInt    *childIDsShifted;
872     PetscSection pSecShifted;
873 
874     PetscCall(PetscSectionGetChart(pSec, &pStart, &pEnd));
875     PetscCall(DMPlexGetDepth(dm, &depth));
876     pStartShifted = DMPlexShiftPoint_Internal(pStart, depth, depthShift);
877     pEndShifted   = DMPlexShiftPoint_Internal(pEnd, depth, depthShift);
878     PetscCall(PetscMalloc2(pEndShifted - pStartShifted, &parentsShifted, pEndShifted - pStartShifted, &childIDsShifted));
879     PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dmNew), &pSecShifted));
880     PetscCall(PetscSectionSetChart(pSecShifted, pStartShifted, pEndShifted));
881     for (p = pStartShifted; p < pEndShifted; p++) {
882       /* start off assuming no children */
883       PetscCall(PetscSectionSetDof(pSecShifted, p, 0));
884     }
885     for (p = pStart; p < pEnd; p++) {
886       PetscInt dof;
887       PetscInt pNew = DMPlexShiftPoint_Internal(p, depth, depthShift);
888 
889       PetscCall(PetscSectionGetDof(pSec, p, &dof));
890       PetscCall(PetscSectionSetDof(pSecShifted, pNew, dof));
891     }
892     PetscCall(PetscSectionSetUp(pSecShifted));
893     for (p = pStart; p < pEnd; p++) {
894       PetscInt dof;
895       PetscInt pNew = DMPlexShiftPoint_Internal(p, depth, depthShift);
896 
897       PetscCall(PetscSectionGetDof(pSec, p, &dof));
898       if (dof) {
899         PetscInt off, offNew;
900 
901         PetscCall(PetscSectionGetOffset(pSec, p, &off));
902         PetscCall(PetscSectionGetOffset(pSecShifted, pNew, &offNew));
903         parentsShifted[offNew]  = DMPlexShiftPoint_Internal(parents[off], depth, depthShift);
904         childIDsShifted[offNew] = childIDs[off];
905       }
906     }
907     PetscCall(DMPlexSetTree(dmNew, pSecShifted, parentsShifted, childIDsShifted));
908     PetscCall(PetscFree2(parentsShifted, childIDsShifted));
909     PetscCall(PetscSectionDestroy(&pSecShifted));
910   }
911   PetscFunctionReturn(PETSC_SUCCESS);
912 }
913 
914 static PetscErrorCode DMPlexConstructGhostCells_Internal(DM dm, DMLabel label, PetscInt *numGhostCells, DM gdm)
915 {
916   PetscSF          sf;
917   IS               valueIS;
918   const PetscInt  *values, *leaves;
919   PetscInt        *depthShift;
920   PetscInt         d, depth = 0, nleaves, loc, Ng, numFS, fs, fStart, fEnd, ghostCell, cEnd, c;
921   const PetscReal *maxCell, *Lstart, *L;
922 
923   PetscFunctionBegin;
924   PetscCall(DMGetPointSF(dm, &sf));
925   PetscCall(PetscSFGetGraph(sf, NULL, &nleaves, &leaves, NULL));
926   nleaves = PetscMax(0, nleaves);
927   PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
928   /* Count ghost cells */
929   PetscCall(DMLabelGetValueIS(label, &valueIS));
930   PetscCall(ISGetLocalSize(valueIS, &numFS));
931   PetscCall(ISGetIndices(valueIS, &values));
932   Ng = 0;
933   for (fs = 0; fs < numFS; ++fs) {
934     IS              faceIS;
935     const PetscInt *faces;
936     PetscInt        numFaces, f, numBdFaces = 0;
937 
938     PetscCall(DMLabelGetStratumIS(label, values[fs], &faceIS));
939     PetscCall(ISGetLocalSize(faceIS, &numFaces));
940     PetscCall(ISGetIndices(faceIS, &faces));
941     for (f = 0; f < numFaces; ++f) {
942       PetscInt numChildren;
943 
944       PetscCall(PetscFindInt(faces[f], nleaves, leaves, &loc));
945       PetscCall(DMPlexGetTreeChildren(dm, faces[f], &numChildren, NULL));
946       /* non-local and ancestors points don't get to register ghosts */
947       if (loc >= 0 || numChildren) continue;
948       if (faces[f] >= fStart && faces[f] < fEnd) ++numBdFaces;
949     }
950     Ng += numBdFaces;
951     PetscCall(ISRestoreIndices(faceIS, &faces));
952     PetscCall(ISDestroy(&faceIS));
953   }
954   PetscCall(DMPlexGetDepth(dm, &depth));
955   PetscCall(PetscMalloc1(2 * (depth + 1), &depthShift));
956   for (d = 0; d <= depth; d++) {
957     PetscInt dEnd;
958 
959     PetscCall(DMPlexGetDepthStratum(dm, d, NULL, &dEnd));
960     depthShift[2 * d]     = dEnd;
961     depthShift[2 * d + 1] = 0;
962   }
963   if (depth >= 0) depthShift[2 * depth + 1] = Ng;
964   PetscCall(DMPlexShiftPointSetUp_Internal(depth, depthShift));
965   PetscCall(DMPlexShiftSizes_Internal(dm, depthShift, gdm));
966   /* Step 3: Set cone/support sizes for new points */
967   PetscCall(DMPlexGetHeightStratum(dm, 0, NULL, &cEnd));
968   for (c = cEnd; c < cEnd + Ng; ++c) PetscCall(DMPlexSetConeSize(gdm, c, 1));
969   for (fs = 0; fs < numFS; ++fs) {
970     IS              faceIS;
971     const PetscInt *faces;
972     PetscInt        numFaces, f;
973 
974     PetscCall(DMLabelGetStratumIS(label, values[fs], &faceIS));
975     PetscCall(ISGetLocalSize(faceIS, &numFaces));
976     PetscCall(ISGetIndices(faceIS, &faces));
977     for (f = 0; f < numFaces; ++f) {
978       PetscInt size, numChildren;
979 
980       PetscCall(PetscFindInt(faces[f], nleaves, leaves, &loc));
981       PetscCall(DMPlexGetTreeChildren(dm, faces[f], &numChildren, NULL));
982       if (loc >= 0 || numChildren) continue;
983       if ((faces[f] < fStart) || (faces[f] >= fEnd)) continue;
984       PetscCall(DMPlexGetSupportSize(dm, faces[f], &size));
985       PetscCheck(size == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "DM has boundary face %" PetscInt_FMT " with %" PetscInt_FMT " support cells", faces[f], size);
986       PetscCall(DMPlexSetSupportSize(gdm, faces[f] + Ng, 2));
987     }
988     PetscCall(ISRestoreIndices(faceIS, &faces));
989     PetscCall(ISDestroy(&faceIS));
990   }
991   /* Step 4: Setup ghosted DM */
992   PetscCall(DMSetUp(gdm));
993   PetscCall(DMPlexShiftPoints_Internal(dm, depthShift, gdm));
994   /* Step 6: Set cones and supports for new points */
995   ghostCell = cEnd;
996   for (fs = 0; fs < numFS; ++fs) {
997     IS              faceIS;
998     const PetscInt *faces;
999     PetscInt        numFaces, f;
1000 
1001     PetscCall(DMLabelGetStratumIS(label, values[fs], &faceIS));
1002     PetscCall(ISGetLocalSize(faceIS, &numFaces));
1003     PetscCall(ISGetIndices(faceIS, &faces));
1004     for (f = 0; f < numFaces; ++f) {
1005       PetscInt newFace = faces[f] + Ng, numChildren;
1006 
1007       PetscCall(PetscFindInt(faces[f], nleaves, leaves, &loc));
1008       PetscCall(DMPlexGetTreeChildren(dm, faces[f], &numChildren, NULL));
1009       if (loc >= 0 || numChildren) continue;
1010       if ((faces[f] < fStart) || (faces[f] >= fEnd)) continue;
1011       PetscCall(DMPlexSetCone(gdm, ghostCell, &newFace));
1012       PetscCall(DMPlexInsertSupport(gdm, newFace, 1, ghostCell));
1013       ++ghostCell;
1014     }
1015     PetscCall(ISRestoreIndices(faceIS, &faces));
1016     PetscCall(ISDestroy(&faceIS));
1017   }
1018   PetscCall(ISRestoreIndices(valueIS, &values));
1019   PetscCall(ISDestroy(&valueIS));
1020   PetscCall(DMPlexShiftCoordinates_Internal(dm, depthShift, gdm));
1021   PetscCall(DMPlexShiftSF_Internal(dm, depthShift, gdm));
1022   PetscCall(DMPlexShiftLabels_Internal(dm, depthShift, gdm));
1023   PetscCall(DMPlexCreateVTKLabel_Internal(dm, PETSC_TRUE, gdm));
1024   PetscCall(DMPlexShiftTree_Internal(dm, depthShift, gdm));
1025   PetscCall(PetscFree(depthShift));
1026   for (c = cEnd; c < cEnd + Ng; ++c) PetscCall(DMPlexSetCellType(gdm, c, DM_POLYTOPE_FV_GHOST));
1027   /* Step 7: Periodicity */
1028   PetscCall(DMGetPeriodicity(dm, &maxCell, &Lstart, &L));
1029   PetscCall(DMSetPeriodicity(gdm, maxCell, Lstart, L));
1030   if (numGhostCells) *numGhostCells = Ng;
1031   PetscFunctionReturn(PETSC_SUCCESS);
1032 }
1033 
1034 /*@
1035   DMPlexConstructGhostCells - Construct ghost cells which connect to every boundary face
1036 
1037   Collective
1038 
1039   Input Parameters:
1040 + dm        - The original `DM`
1041 - labelName - The label specifying the boundary faces, or "Face Sets" if this is `NULL`
1042 
1043   Output Parameters:
1044 + numGhostCells - The number of ghost cells added to the `DM`
1045 - dmGhosted     - The new `DM`
1046 
1047   Level: developer
1048 
1049   Note:
1050   If no label exists of that name, one will be created marking all boundary faces
1051 
1052 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`
1053 @*/
1054 PetscErrorCode DMPlexConstructGhostCells(DM dm, const char labelName[], PetscInt *numGhostCells, DM *dmGhosted)
1055 {
1056   DM          gdm;
1057   DMLabel     label;
1058   const char *name = labelName ? labelName : "Face Sets";
1059   PetscInt    dim, Ng = 0;
1060   PetscBool   useCone, useClosure;
1061 
1062   PetscFunctionBegin;
1063   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1064   if (numGhostCells) PetscAssertPointer(numGhostCells, 3);
1065   PetscAssertPointer(dmGhosted, 4);
1066   PetscCall(DMCreate(PetscObjectComm((PetscObject)dm), &gdm));
1067   PetscCall(DMSetType(gdm, DMPLEX));
1068   PetscCall(DMGetDimension(dm, &dim));
1069   PetscCall(DMSetDimension(gdm, dim));
1070   PetscCall(DMGetBasicAdjacency(dm, &useCone, &useClosure));
1071   PetscCall(DMSetBasicAdjacency(gdm, useCone, useClosure));
1072   PetscCall(DMGetLabel(dm, name, &label));
1073   if (!label) {
1074     /* Get label for boundary faces */
1075     PetscCall(DMCreateLabel(dm, name));
1076     PetscCall(DMGetLabel(dm, name, &label));
1077     PetscCall(DMPlexMarkBoundaryFaces(dm, 1, label));
1078   }
1079   PetscCall(DMPlexConstructGhostCells_Internal(dm, label, &Ng, gdm));
1080   PetscCall(DMCopyDisc(dm, gdm));
1081   PetscCall(DMPlexCopy_Internal(dm, PETSC_TRUE, PETSC_TRUE, gdm));
1082   gdm->setfromoptionscalled = dm->setfromoptionscalled;
1083   if (numGhostCells) *numGhostCells = Ng;
1084   *dmGhosted = gdm;
1085   PetscFunctionReturn(PETSC_SUCCESS);
1086 }
1087 
1088 static PetscErrorCode DivideCells_Private(DM dm, DMLabel label, DMPlexPointQueue queue)
1089 {
1090   PetscInt dim, d, shift = 100, *pStart, *pEnd;
1091 
1092   PetscFunctionBegin;
1093   PetscCall(DMGetDimension(dm, &dim));
1094   PetscCall(PetscMalloc2(dim, &pStart, dim, &pEnd));
1095   for (d = 0; d < dim; ++d) PetscCall(DMPlexGetDepthStratum(dm, d, &pStart[d], &pEnd[d]));
1096   while (!DMPlexPointQueueEmpty(queue)) {
1097     PetscInt  cell    = -1;
1098     PetscInt *closure = NULL;
1099     PetscInt  closureSize, cl, cval;
1100 
1101     PetscCall(DMPlexPointQueueDequeue(queue, &cell));
1102     PetscCall(DMLabelGetValue(label, cell, &cval));
1103     PetscCall(DMPlexGetTransitiveClosure(dm, cell, PETSC_TRUE, &closureSize, &closure));
1104     /* Mark points in the cell closure that touch the fault */
1105     for (d = 0; d < dim; ++d) {
1106       for (cl = 0; cl < closureSize * 2; cl += 2) {
1107         const PetscInt clp = closure[cl];
1108         PetscInt       clval;
1109 
1110         if ((clp < pStart[d]) || (clp >= pEnd[d])) continue;
1111         PetscCall(DMLabelGetValue(label, clp, &clval));
1112         if (clval == -1) {
1113           const PetscInt *cone;
1114           PetscInt        coneSize, c;
1115 
1116           /* If a cone point touches the fault, then this point touches the fault */
1117           PetscCall(DMPlexGetCone(dm, clp, &cone));
1118           PetscCall(DMPlexGetConeSize(dm, clp, &coneSize));
1119           for (c = 0; c < coneSize; ++c) {
1120             PetscInt cpval;
1121 
1122             PetscCall(DMLabelGetValue(label, cone[c], &cpval));
1123             if (cpval != -1) {
1124               PetscInt dep;
1125 
1126               PetscCall(DMPlexGetPointDepth(dm, clp, &dep));
1127               clval = cval < 0 ? -(shift + dep) : shift + dep;
1128               PetscCall(DMLabelSetValue(label, clp, clval));
1129               break;
1130             }
1131           }
1132         }
1133         /* Mark neighbor cells through marked faces (these cells must also touch the fault) */
1134         if (d == dim - 1 && clval != -1) {
1135           const PetscInt *support;
1136           PetscInt        supportSize, s, nval;
1137 
1138           PetscCall(DMPlexGetSupport(dm, clp, &support));
1139           PetscCall(DMPlexGetSupportSize(dm, clp, &supportSize));
1140           for (s = 0; s < supportSize; ++s) {
1141             PetscCall(DMLabelGetValue(label, support[s], &nval));
1142             if (nval == -1) {
1143               PetscCall(DMLabelSetValue(label, support[s], clval < 0 ? clval - 1 : clval + 1));
1144               PetscCall(DMPlexPointQueueEnqueue(queue, support[s]));
1145             }
1146           }
1147         }
1148       }
1149     }
1150     PetscCall(DMPlexRestoreTransitiveClosure(dm, cell, PETSC_TRUE, &closureSize, &closure));
1151   }
1152   PetscCall(PetscFree2(pStart, pEnd));
1153   PetscFunctionReturn(PETSC_SUCCESS);
1154 }
1155 
1156 typedef struct {
1157   DM               dm;
1158   DMPlexPointQueue queue;
1159 } PointDivision;
1160 
1161 static PetscErrorCode divideCell(DMLabel label, PetscInt p, PetscInt val, void *ctx)
1162 {
1163   PointDivision  *div  = (PointDivision *)ctx;
1164   PetscInt        cval = val < 0 ? val - 1 : val + 1;
1165   const PetscInt *support;
1166   PetscInt        supportSize, s;
1167 
1168   PetscFunctionBegin;
1169   PetscCall(DMPlexGetSupport(div->dm, p, &support));
1170   PetscCall(DMPlexGetSupportSize(div->dm, p, &supportSize));
1171   for (s = 0; s < supportSize; ++s) {
1172     PetscCall(DMLabelSetValue(label, support[s], cval));
1173     PetscCall(DMPlexPointQueueEnqueue(div->queue, support[s]));
1174   }
1175   PetscFunctionReturn(PETSC_SUCCESS);
1176 }
1177 
1178 /* Mark cells by label propagation */
1179 static PetscErrorCode DMPlexLabelFaultHalo(DM dm, DMLabel faultLabel)
1180 {
1181   DMPlexPointQueue queue = NULL;
1182   PointDivision    div;
1183   PetscSF          pointSF;
1184   IS               pointIS;
1185   const PetscInt  *points;
1186   PetscBool        empty;
1187   PetscInt         dim, shift = 100, n, i;
1188 
1189   PetscFunctionBegin;
1190   PetscCall(DMGetDimension(dm, &dim));
1191   PetscCall(DMPlexPointQueueCreate(1024, &queue));
1192   div.dm    = dm;
1193   div.queue = queue;
1194   /* Enqueue cells on fault */
1195   PetscCall(DMLabelGetStratumIS(faultLabel, shift + dim, &pointIS));
1196   if (pointIS) {
1197     PetscCall(ISGetLocalSize(pointIS, &n));
1198     PetscCall(ISGetIndices(pointIS, &points));
1199     for (i = 0; i < n; ++i) PetscCall(DMPlexPointQueueEnqueue(queue, points[i]));
1200     PetscCall(ISRestoreIndices(pointIS, &points));
1201     PetscCall(ISDestroy(&pointIS));
1202   }
1203   PetscCall(DMLabelGetStratumIS(faultLabel, -(shift + dim), &pointIS));
1204   if (pointIS) {
1205     PetscCall(ISGetLocalSize(pointIS, &n));
1206     PetscCall(ISGetIndices(pointIS, &points));
1207     for (i = 0; i < n; ++i) PetscCall(DMPlexPointQueueEnqueue(queue, points[i]));
1208     PetscCall(ISRestoreIndices(pointIS, &points));
1209     PetscCall(ISDestroy(&pointIS));
1210   }
1211 
1212   PetscCall(DMGetPointSF(dm, &pointSF));
1213   PetscCall(DMLabelPropagateBegin(faultLabel, pointSF));
1214   /* While edge queue is not empty: */
1215   PetscCall(DMPlexPointQueueEmptyCollective((PetscObject)dm, queue, &empty));
1216   while (!empty) {
1217     PetscCall(DivideCells_Private(dm, faultLabel, queue));
1218     PetscCall(DMLabelPropagatePush(faultLabel, pointSF, divideCell, &div));
1219     PetscCall(DMPlexPointQueueEmptyCollective((PetscObject)dm, queue, &empty));
1220   }
1221   PetscCall(DMLabelPropagateEnd(faultLabel, pointSF));
1222   PetscCall(DMPlexPointQueueDestroy(&queue));
1223   PetscFunctionReturn(PETSC_SUCCESS);
1224 }
1225 
1226 /*
1227   We are adding three kinds of points here:
1228     Replicated:     Copies of points which exist in the mesh, such as vertices identified across a fault
1229     Non-replicated: Points which exist on the fault, but are not replicated
1230     Ghost:          These are shared fault faces which are not owned by this process. These do not produce hybrid cells and do not replicate
1231     Hybrid:         Entirely new points, such as cohesive cells
1232 
1233   When creating subsequent cohesive cells, we shift the old hybrid cells to the end of the numbering at
1234   each depth so that the new split/hybrid points can be inserted as a block.
1235 */
1236 static PetscErrorCode DMPlexConstructCohesiveCells_Internal(DM dm, DMLabel label, DMLabel splitLabel, DM sdm)
1237 {
1238   MPI_Comm         comm;
1239   IS               valueIS;
1240   PetscInt         numSP = 0; /* The number of depths for which we have replicated points */
1241   const PetscInt  *values;    /* List of depths for which we have replicated points */
1242   IS              *splitIS;
1243   IS              *unsplitIS;
1244   IS               ghostIS;
1245   PetscInt        *numSplitPoints;     /* The number of replicated points at each depth */
1246   PetscInt        *numUnsplitPoints;   /* The number of non-replicated points at each depth which still give rise to hybrid points */
1247   PetscInt        *numHybridPoints;    /* The number of new hybrid points at each depth */
1248   PetscInt        *numHybridPointsOld; /* The number of existing hybrid points at each depth */
1249   PetscInt         numGhostPoints;     /* The number of unowned, shared fault faces */
1250   const PetscInt **splitPoints;        /* Replicated points for each depth */
1251   const PetscInt **unsplitPoints;      /* Non-replicated points for each depth */
1252   const PetscInt  *ghostPoints;        /* Ghost fault faces */
1253   PetscSection     coordSection;
1254   Vec              coordinates;
1255   PetscScalar     *coords;
1256   PetscInt        *depthMax;   /* The first hybrid point at each depth in the original mesh */
1257   PetscInt        *depthEnd;   /* The point limit at each depth in the original mesh */
1258   PetscInt        *depthShift; /* Number of replicated+hybrid points at each depth */
1259   PetscInt        *pMaxNew;    /* The first replicated point at each depth in the new mesh, hybrids come after this */
1260   PetscInt        *coneNew, *coneONew, *supportNew;
1261   PetscInt         shift = 100, shift2 = 200, depth = 0, dep, dim, d, sp, maxConeSize, maxSupportSize, maxConeSizeNew, maxSupportSizeNew, numLabels, vStart, vEnd, pEnd, p, v;
1262 
1263   PetscFunctionBegin;
1264   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1265   PetscCall(DMGetDimension(dm, &dim));
1266   PetscCall(DMPlexGetDepth(dm, &depth));
1267   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1268   /* We do not want this label automatically computed, instead we compute it here */
1269   PetscCall(DMCreateLabel(sdm, "celltype"));
1270   /* Count split points and add cohesive cells */
1271   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
1272   PetscCall(PetscMalloc5(depth + 1, &depthMax, depth + 1, &depthEnd, 2 * (depth + 1), &depthShift, depth + 1, &pMaxNew, depth + 1, &numHybridPointsOld));
1273   PetscCall(PetscMalloc7(depth + 1, &splitIS, depth + 1, &unsplitIS, depth + 1, &numSplitPoints, depth + 1, &numUnsplitPoints, depth + 1, &numHybridPoints, depth + 1, &splitPoints, depth + 1, &unsplitPoints));
1274   for (d = 0; d <= depth; ++d) {
1275     PetscCall(DMPlexGetDepthStratum(dm, d, NULL, &pMaxNew[d]));
1276     PetscCall(DMPlexGetTensorPrismBounds_Internal(dm, d, &depthMax[d], NULL));
1277     depthEnd[d]           = pMaxNew[d];
1278     depthMax[d]           = depthMax[d] < 0 ? depthEnd[d] : depthMax[d];
1279     numSplitPoints[d]     = 0;
1280     numUnsplitPoints[d]   = 0;
1281     numHybridPoints[d]    = 0;
1282     numHybridPointsOld[d] = depthMax[d] < 0 ? 0 : depthEnd[d] - depthMax[d];
1283     splitPoints[d]        = NULL;
1284     unsplitPoints[d]      = NULL;
1285     splitIS[d]            = NULL;
1286     unsplitIS[d]          = NULL;
1287     /* we are shifting the existing hybrid points with the stratum behind them, so
1288      * the split comes at the end of the normal points, i.e., at depthMax[d] */
1289     depthShift[2 * d]     = depthMax[d];
1290     depthShift[2 * d + 1] = 0;
1291   }
1292   numGhostPoints = 0;
1293   ghostPoints    = NULL;
1294   if (label) {
1295     PetscCall(DMLabelGetValueIS(label, &valueIS));
1296     PetscCall(ISGetLocalSize(valueIS, &numSP));
1297     PetscCall(ISGetIndices(valueIS, &values));
1298   }
1299   for (sp = 0; sp < numSP; ++sp) {
1300     const PetscInt dep = values[sp];
1301 
1302     if ((dep < 0) || (dep > depth)) continue;
1303     PetscCall(DMLabelGetStratumIS(label, dep, &splitIS[dep]));
1304     if (splitIS[dep]) {
1305       PetscCall(ISGetLocalSize(splitIS[dep], &numSplitPoints[dep]));
1306       PetscCall(ISGetIndices(splitIS[dep], &splitPoints[dep]));
1307     }
1308     PetscCall(DMLabelGetStratumIS(label, shift2 + dep, &unsplitIS[dep]));
1309     if (unsplitIS[dep]) {
1310       PetscCall(ISGetLocalSize(unsplitIS[dep], &numUnsplitPoints[dep]));
1311       PetscCall(ISGetIndices(unsplitIS[dep], &unsplitPoints[dep]));
1312     }
1313   }
1314   PetscCall(DMLabelGetStratumIS(label, shift2 + dim - 1, &ghostIS));
1315   if (ghostIS) {
1316     PetscCall(ISGetLocalSize(ghostIS, &numGhostPoints));
1317     PetscCall(ISGetIndices(ghostIS, &ghostPoints));
1318   }
1319   /* Calculate number of hybrid points */
1320   for (d = 1; d <= depth; ++d) numHybridPoints[d] = numSplitPoints[d - 1] + numUnsplitPoints[d - 1]; /* There is a hybrid cell/face/edge for every split face/edge/vertex   */
1321   for (d = 0; d <= depth; ++d) depthShift[2 * d + 1] = numSplitPoints[d] + numHybridPoints[d];
1322   PetscCall(DMPlexShiftPointSetUp_Internal(depth, depthShift));
1323   /* the end of the points in this stratum that come before the new points:
1324    * shifting pMaxNew[d] gets the new start of the next stratum, then count back the old hybrid points and the newly
1325    * added points */
1326   for (d = 0; d <= depth; ++d) pMaxNew[d] = DMPlexShiftPoint_Internal(pMaxNew[d], depth, depthShift) - (numHybridPointsOld[d] + numSplitPoints[d] + numHybridPoints[d]);
1327   PetscCall(DMPlexShiftSizes_Internal(dm, depthShift, sdm));
1328   /* Step 3: Set cone/support sizes for new points */
1329   for (dep = 0; dep <= depth; ++dep) {
1330     for (p = 0; p < numSplitPoints[dep]; ++p) {
1331       const PetscInt  oldp   = splitPoints[dep][p];
1332       const PetscInt  newp   = DMPlexShiftPoint_Internal(oldp, depth, depthShift) /*oldp + depthOffset[dep]*/;
1333       const PetscInt  splitp = p + pMaxNew[dep];
1334       const PetscInt *support;
1335       DMPolytopeType  ct;
1336       PetscInt        coneSize, supportSize, qf, qn, qp, e;
1337 
1338       PetscCall(DMPlexGetConeSize(dm, oldp, &coneSize));
1339       PetscCall(DMPlexSetConeSize(sdm, splitp, coneSize));
1340       PetscCall(DMPlexGetSupportSize(dm, oldp, &supportSize));
1341       PetscCall(DMPlexSetSupportSize(sdm, splitp, supportSize));
1342       PetscCall(DMPlexGetCellType(dm, oldp, &ct));
1343       PetscCall(DMPlexSetCellType(sdm, splitp, ct));
1344       if (dep == depth - 1) {
1345         const PetscInt hybcell = p + pMaxNew[dep + 1] + numSplitPoints[dep + 1];
1346 
1347         /* Add cohesive cells, they are prisms */
1348         PetscCall(DMPlexSetConeSize(sdm, hybcell, 2 + coneSize));
1349         switch (coneSize) {
1350         case 2:
1351           PetscCall(DMPlexSetCellType(sdm, hybcell, DM_POLYTOPE_SEG_PRISM_TENSOR));
1352           break;
1353         case 3:
1354           PetscCall(DMPlexSetCellType(sdm, hybcell, DM_POLYTOPE_TRI_PRISM_TENSOR));
1355           break;
1356         case 4:
1357           PetscCall(DMPlexSetCellType(sdm, hybcell, DM_POLYTOPE_QUAD_PRISM_TENSOR));
1358           break;
1359         }
1360         /* Shared fault faces with only one support cell now have two with the cohesive cell */
1361         /*   TODO Check thaat oldp has rootdegree == 1 */
1362         if (supportSize == 1) {
1363           const PetscInt *support;
1364           PetscInt        val;
1365 
1366           PetscCall(DMPlexGetSupport(dm, oldp, &support));
1367           PetscCall(DMLabelGetValue(label, support[0], &val));
1368           if (val < 0) PetscCall(DMPlexSetSupportSize(sdm, splitp, 2));
1369           else PetscCall(DMPlexSetSupportSize(sdm, newp, 2));
1370         }
1371       } else if (dep == 0) {
1372         const PetscInt hybedge = p + pMaxNew[dep + 1] + numSplitPoints[dep + 1];
1373 
1374         PetscCall(DMPlexGetSupport(dm, oldp, &support));
1375         for (e = 0, qn = 0, qp = 0, qf = 0; e < supportSize; ++e) {
1376           PetscInt val;
1377 
1378           PetscCall(DMLabelGetValue(label, support[e], &val));
1379           if (val == 1) ++qf;
1380           if ((val == 1) || (val == (shift + 1))) ++qn;
1381           if ((val == 1) || (val == -(shift + 1))) ++qp;
1382         }
1383         /* Split old vertex: Edges into original vertex and new cohesive edge */
1384         PetscCall(DMPlexSetSupportSize(sdm, newp, qn + 1));
1385         /* Split new vertex: Edges into split vertex and new cohesive edge */
1386         PetscCall(DMPlexSetSupportSize(sdm, splitp, qp + 1));
1387         /* Add hybrid edge */
1388         PetscCall(DMPlexSetConeSize(sdm, hybedge, 2));
1389         PetscCall(DMPlexSetSupportSize(sdm, hybedge, qf));
1390         PetscCall(DMPlexSetCellType(sdm, hybedge, DM_POLYTOPE_POINT_PRISM_TENSOR));
1391       } else if (dep == dim - 2) {
1392         const PetscInt hybface = p + pMaxNew[dep + 1] + numSplitPoints[dep + 1];
1393 
1394         PetscCall(DMPlexGetSupport(dm, oldp, &support));
1395         for (e = 0, qn = 0, qp = 0, qf = 0; e < supportSize; ++e) {
1396           PetscInt val;
1397 
1398           PetscCall(DMLabelGetValue(label, support[e], &val));
1399           if (val == dim - 1) ++qf;
1400           if ((val == dim - 1) || (val == (shift + dim - 1))) ++qn;
1401           if ((val == dim - 1) || (val == -(shift + dim - 1))) ++qp;
1402         }
1403         /* Split old edge: Faces into original edge and cohesive face (positive side?) */
1404         PetscCall(DMPlexSetSupportSize(sdm, newp, qn + 1));
1405         /* Split new edge: Faces into split edge and cohesive face (negative side?) */
1406         PetscCall(DMPlexSetSupportSize(sdm, splitp, qp + 1));
1407         /* Add hybrid face */
1408         PetscCall(DMPlexSetConeSize(sdm, hybface, 4));
1409         PetscCall(DMPlexSetSupportSize(sdm, hybface, qf));
1410         PetscCall(DMPlexSetCellType(sdm, hybface, DM_POLYTOPE_SEG_PRISM_TENSOR));
1411       }
1412     }
1413   }
1414   for (dep = 0; dep <= depth; ++dep) {
1415     for (p = 0; p < numUnsplitPoints[dep]; ++p) {
1416       const PetscInt  oldp = unsplitPoints[dep][p];
1417       const PetscInt  newp = DMPlexShiftPoint_Internal(oldp, depth, depthShift) /*oldp + depthOffset[dep]*/;
1418       const PetscInt *support;
1419       PetscInt        coneSize, supportSize, qf, e, s;
1420 
1421       PetscCall(DMPlexGetConeSize(dm, oldp, &coneSize));
1422       PetscCall(DMPlexGetSupportSize(dm, oldp, &supportSize));
1423       PetscCall(DMPlexGetSupport(dm, oldp, &support));
1424       if (dep == 0) {
1425         const PetscInt hybedge = p + pMaxNew[dep + 1] + numSplitPoints[dep + 1] + numSplitPoints[dep];
1426 
1427         /* Unsplit vertex: Edges into original vertex, split edges, and new cohesive edge twice */
1428         for (s = 0, qf = 0; s < supportSize; ++s, ++qf) {
1429           PetscCall(PetscFindInt(support[s], numSplitPoints[dep + 1], splitPoints[dep + 1], &e));
1430           if (e >= 0) ++qf;
1431         }
1432         PetscCall(DMPlexSetSupportSize(sdm, newp, qf + 2));
1433         /* Add hybrid edge */
1434         PetscCall(DMPlexSetConeSize(sdm, hybedge, 2));
1435         for (e = 0, qf = 0; e < supportSize; ++e) {
1436           PetscInt val;
1437 
1438           PetscCall(DMLabelGetValue(label, support[e], &val));
1439           /* Split and unsplit edges produce hybrid faces */
1440           if (val == 1) ++qf;
1441           if (val == (shift2 + 1)) ++qf;
1442         }
1443         PetscCall(DMPlexSetSupportSize(sdm, hybedge, qf));
1444         PetscCall(DMPlexSetCellType(sdm, hybedge, DM_POLYTOPE_POINT_PRISM_TENSOR));
1445       } else if (dep == dim - 2) {
1446         const PetscInt hybface = p + pMaxNew[dep + 1] + numSplitPoints[dep + 1] + numSplitPoints[dep];
1447         PetscInt       val;
1448 
1449         for (e = 0, qf = 0; e < supportSize; ++e) {
1450           PetscCall(DMLabelGetValue(label, support[e], &val));
1451           if (val == dim - 1) qf += 2;
1452           else ++qf;
1453         }
1454         /* Unsplit edge: Faces into original edge, split face, and cohesive face twice */
1455         PetscCall(DMPlexSetSupportSize(sdm, newp, qf + 2));
1456         /* Add hybrid face */
1457         for (e = 0, qf = 0; e < supportSize; ++e) {
1458           PetscCall(DMLabelGetValue(label, support[e], &val));
1459           if (val == dim - 1) ++qf;
1460         }
1461         PetscCall(DMPlexSetConeSize(sdm, hybface, 4));
1462         PetscCall(DMPlexSetSupportSize(sdm, hybface, qf));
1463         PetscCall(DMPlexSetCellType(sdm, hybface, DM_POLYTOPE_SEG_PRISM_TENSOR));
1464       }
1465     }
1466   }
1467   /* Step 4: Setup split DM */
1468   PetscCall(DMSetUp(sdm));
1469   PetscCall(DMPlexShiftPoints_Internal(dm, depthShift, sdm));
1470   PetscCall(DMPlexGetMaxSizes(sdm, &maxConeSizeNew, &maxSupportSizeNew));
1471   PetscCall(PetscMalloc3(PetscMax(maxConeSize, maxConeSizeNew) * 3, &coneNew, PetscMax(maxConeSize, maxConeSizeNew) * 3, &coneONew, PetscMax(maxSupportSize, maxSupportSizeNew), &supportNew));
1472   /* Step 6: Set cones and supports for new points */
1473   for (dep = 0; dep <= depth; ++dep) {
1474     for (p = 0; p < numSplitPoints[dep]; ++p) {
1475       const PetscInt  oldp   = splitPoints[dep][p];
1476       const PetscInt  newp   = DMPlexShiftPoint_Internal(oldp, depth, depthShift) /*oldp + depthOffset[dep]*/;
1477       const PetscInt  splitp = p + pMaxNew[dep];
1478       const PetscInt *cone, *support, *ornt;
1479       DMPolytopeType  ct;
1480       PetscInt        coneSize, supportSize, q, qf, qn, qp, v, e, s;
1481 
1482       PetscCall(DMPlexGetCellType(dm, oldp, &ct));
1483       PetscCall(DMPlexGetConeSize(dm, oldp, &coneSize));
1484       PetscCall(DMPlexGetCone(dm, oldp, &cone));
1485       PetscCall(DMPlexGetConeOrientation(dm, oldp, &ornt));
1486       PetscCall(DMPlexGetSupportSize(dm, oldp, &supportSize));
1487       PetscCall(DMPlexGetSupport(dm, oldp, &support));
1488       if (dep == depth - 1) {
1489         PetscBool       hasUnsplit = PETSC_FALSE;
1490         const PetscInt  hybcell    = p + pMaxNew[dep + 1] + numSplitPoints[dep + 1];
1491         const PetscInt *supportF;
1492 
1493         coneONew[0] = coneONew[1] = -1000;
1494         /* Split face:       copy in old face to new face to start */
1495         PetscCall(DMPlexGetSupport(sdm, newp, &supportF));
1496         PetscCall(DMPlexSetSupport(sdm, splitp, supportF));
1497         /* Split old face:   old vertices/edges in cone so no change */
1498         /* Split new face:   new vertices/edges in cone */
1499         for (q = 0; q < coneSize; ++q) {
1500           PetscCall(PetscFindInt(cone[q], numSplitPoints[dep - 1], splitPoints[dep - 1], &v));
1501           if (v < 0) {
1502             PetscCall(PetscFindInt(cone[q], numUnsplitPoints[dep - 1], unsplitPoints[dep - 1], &v));
1503             PetscCheck(v >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Could not locate point %" PetscInt_FMT " in split or unsplit points of depth %" PetscInt_FMT, cone[q], dep - 1);
1504             coneNew[2 + q] = DMPlexShiftPoint_Internal(cone[q], depth, depthShift) /*cone[q] + depthOffset[dep-1]*/;
1505             hasUnsplit     = PETSC_TRUE;
1506           } else {
1507             coneNew[2 + q] = v + pMaxNew[dep - 1];
1508             if (dep > 1) {
1509               const PetscInt *econe;
1510               PetscInt        econeSize, r, vs, vu;
1511 
1512               PetscCall(DMPlexGetConeSize(dm, cone[q], &econeSize));
1513               PetscCall(DMPlexGetCone(dm, cone[q], &econe));
1514               for (r = 0; r < econeSize; ++r) {
1515                 PetscCall(PetscFindInt(econe[r], numSplitPoints[dep - 2], splitPoints[dep - 2], &vs));
1516                 PetscCall(PetscFindInt(econe[r], numUnsplitPoints[dep - 2], unsplitPoints[dep - 2], &vu));
1517                 if (vs >= 0) continue;
1518                 PetscCheck(vu >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Could not locate point %" PetscInt_FMT " in split or unsplit points of depth %" PetscInt_FMT, econe[r], dep - 2);
1519                 hasUnsplit = PETSC_TRUE;
1520               }
1521             }
1522           }
1523         }
1524         PetscCall(DMPlexSetCone(sdm, splitp, &coneNew[2]));
1525         PetscCall(DMPlexSetConeOrientation(sdm, splitp, ornt));
1526         /* Face support */
1527         PetscInt vals[2];
1528 
1529         PetscCall(DMLabelGetValue(label, support[0], &vals[0]));
1530         if (supportSize > 1) PetscCall(DMLabelGetValue(label, support[1], &vals[1]));
1531         else vals[1] = -vals[0];
1532         PetscCheck(vals[0] * vals[1] < 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid support labels %" PetscInt_FMT " %" PetscInt_FMT, vals[0], vals[1]);
1533 
1534         for (s = 0; s < 2; ++s) {
1535           if (s >= supportSize) {
1536             if (vals[s] < 0) {
1537               /* Ghost old face:   Replace negative side cell with cohesive cell */
1538               PetscCall(DMPlexInsertSupport(sdm, newp, s, hybcell));
1539             } else {
1540               /* Ghost new face:   Replace positive side cell with cohesive cell */
1541               PetscCall(DMPlexInsertSupport(sdm, splitp, s, hybcell));
1542             }
1543           } else {
1544             if (vals[s] < 0) {
1545               /* Split old face:   Replace negative side cell with cohesive cell */
1546               PetscCall(DMPlexInsertSupport(sdm, newp, s, hybcell));
1547             } else {
1548               /* Split new face:   Replace positive side cell with cohesive cell */
1549               PetscCall(DMPlexInsertSupport(sdm, splitp, s, hybcell));
1550             }
1551           }
1552         }
1553         /* Get orientation for cohesive face using the positive side cell */
1554         {
1555           const PetscInt *ncone, *nconeO;
1556           PetscInt        nconeSize, nc, ocell;
1557           PetscBool       flip = PETSC_FALSE;
1558 
1559           if (supportSize > 1) {
1560             ocell = vals[0] < 0 ? support[1] : support[0];
1561           } else {
1562             ocell = support[0];
1563             flip  = vals[0] < 0 ? PETSC_TRUE : PETSC_FALSE;
1564           }
1565           PetscCall(DMPlexGetConeSize(dm, ocell, &nconeSize));
1566           PetscCall(DMPlexGetCone(dm, ocell, &ncone));
1567           PetscCall(DMPlexGetConeOrientation(dm, ocell, &nconeO));
1568           for (nc = 0; nc < nconeSize; ++nc) {
1569             if (ncone[nc] == oldp) {
1570               coneONew[0] = flip ? -(nconeO[nc] + 1) : nconeO[nc];
1571               break;
1572             }
1573           }
1574           PetscCheck(nc < nconeSize, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Could not locate face %" PetscInt_FMT " in neighboring cell %" PetscInt_FMT, oldp, ocell);
1575         }
1576         /* Cohesive cell:    Old and new split face, then new cohesive faces */
1577         {
1578           const PetscInt No = DMPolytopeTypeGetNumArrangements(ct) / 2;
1579           PetscCheck(coneONew[0] >= -No && coneONew[0] < No, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid %s orientation %" PetscInt_FMT, DMPolytopeTypes[ct], coneONew[0]);
1580         }
1581         const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, coneONew[0]);
1582 
1583         coneNew[0]  = newp; /* Extracted negative side orientation above */
1584         coneNew[1]  = splitp;
1585         coneONew[1] = coneONew[0];
1586         for (q = 0; q < coneSize; ++q) {
1587           /* Hybrid faces must follow order from oriented end face */
1588           const PetscInt qa = arr[q * 2 + 0];
1589           const PetscInt qo = arr[q * 2 + 1];
1590           DMPolytopeType ft = dep == 2 ? DM_POLYTOPE_SEGMENT : DM_POLYTOPE_POINT;
1591 
1592           PetscCall(PetscFindInt(cone[qa], numSplitPoints[dep - 1], splitPoints[dep - 1], &v));
1593           if (v < 0) {
1594             PetscCall(PetscFindInt(cone[qa], numUnsplitPoints[dep - 1], unsplitPoints[dep - 1], &v));
1595             coneNew[2 + q] = v + pMaxNew[dep] + numSplitPoints[dep] + numSplitPoints[dep - 1];
1596           } else {
1597             coneNew[2 + q] = v + pMaxNew[dep] + numSplitPoints[dep];
1598           }
1599           coneONew[2 + q] = DMPolytopeTypeComposeOrientation(ft, qo, ornt[qa]);
1600         }
1601         PetscCall(DMPlexSetCone(sdm, hybcell, coneNew));
1602         PetscCall(DMPlexSetConeOrientation(sdm, hybcell, coneONew));
1603         /* Label the hybrid cells on the boundary of the split */
1604         if (hasUnsplit) PetscCall(DMLabelSetValue(label, -hybcell, dim));
1605       } else if (dep == 0) {
1606         const PetscInt hybedge = p + pMaxNew[dep + 1] + numSplitPoints[dep + 1];
1607 
1608         /* Split old vertex: Edges in old split faces and new cohesive edge */
1609         for (e = 0, qn = 0; e < supportSize; ++e) {
1610           PetscInt val;
1611 
1612           PetscCall(DMLabelGetValue(label, support[e], &val));
1613           if ((val == 1) || (val == (shift + 1))) supportNew[qn++] = DMPlexShiftPoint_Internal(support[e], depth, depthShift) /*support[e] + depthOffset[dep+1]*/;
1614         }
1615         supportNew[qn] = hybedge;
1616         PetscCall(DMPlexSetSupport(sdm, newp, supportNew));
1617         /* Split new vertex: Edges in new split faces and new cohesive edge */
1618         for (e = 0, qp = 0; e < supportSize; ++e) {
1619           PetscInt val, edge;
1620 
1621           PetscCall(DMLabelGetValue(label, support[e], &val));
1622           if (val == 1) {
1623             PetscCall(PetscFindInt(support[e], numSplitPoints[dep + 1], splitPoints[dep + 1], &edge));
1624             PetscCheck(edge >= 0, comm, PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " is not a split edge", support[e]);
1625             supportNew[qp++] = edge + pMaxNew[dep + 1];
1626           } else if (val == -(shift + 1)) {
1627             supportNew[qp++] = DMPlexShiftPoint_Internal(support[e], depth, depthShift) /*support[e] + depthOffset[dep+1]*/;
1628           }
1629         }
1630         supportNew[qp] = hybedge;
1631         PetscCall(DMPlexSetSupport(sdm, splitp, supportNew));
1632         /* Hybrid edge:    Old and new split vertex */
1633         coneNew[0] = newp;
1634         coneNew[1] = splitp;
1635         PetscCall(DMPlexSetCone(sdm, hybedge, coneNew));
1636         for (e = 0, qf = 0; e < supportSize; ++e) {
1637           PetscInt val, edge;
1638 
1639           PetscCall(DMLabelGetValue(label, support[e], &val));
1640           if (val == 1) {
1641             PetscCall(PetscFindInt(support[e], numSplitPoints[dep + 1], splitPoints[dep + 1], &edge));
1642             PetscCheck(edge >= 0, comm, PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " is not a split edge", support[e]);
1643             supportNew[qf++] = edge + pMaxNew[dep + 2] + numSplitPoints[dep + 2];
1644           }
1645         }
1646         PetscCall(DMPlexSetSupport(sdm, hybedge, supportNew));
1647       } else if (dep == dim - 2) {
1648         const PetscInt hybface = p + pMaxNew[dep + 1] + numSplitPoints[dep + 1];
1649 
1650         /* Split old edge:   old vertices in cone so no change */
1651         /* Split new edge:   new vertices in cone */
1652         for (q = 0; q < coneSize; ++q) {
1653           PetscCall(PetscFindInt(cone[q], numSplitPoints[dep - 1], splitPoints[dep - 1], &v));
1654           if (v < 0) {
1655             PetscCall(PetscFindInt(cone[q], numUnsplitPoints[dep - 1], unsplitPoints[dep - 1], &v));
1656             PetscCheck(v >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Could not locate point %" PetscInt_FMT " in split or unsplit points of depth %" PetscInt_FMT, cone[q], dep - 1);
1657             coneNew[q] = DMPlexShiftPoint_Internal(cone[q], depth, depthShift) /*cone[q] + depthOffset[dep-1]*/;
1658           } else {
1659             coneNew[q] = v + pMaxNew[dep - 1];
1660           }
1661         }
1662         PetscCall(DMPlexSetCone(sdm, splitp, coneNew));
1663         /* Split old edge: Faces in positive side cells and old split faces */
1664         for (e = 0, q = 0; e < supportSize; ++e) {
1665           PetscInt val;
1666 
1667           PetscCall(DMLabelGetValue(label, support[e], &val));
1668           if (val == dim - 1) {
1669             supportNew[q++] = DMPlexShiftPoint_Internal(support[e], depth, depthShift) /*support[e] + depthOffset[dep+1]*/;
1670           } else if (val == (shift + dim - 1)) {
1671             supportNew[q++] = DMPlexShiftPoint_Internal(support[e], depth, depthShift) /*support[e] + depthOffset[dep+1]*/;
1672           }
1673         }
1674         supportNew[q++] = p + pMaxNew[dep + 1] + numSplitPoints[dep + 1];
1675         PetscCall(DMPlexSetSupport(sdm, newp, supportNew));
1676         /* Split new edge: Faces in negative side cells and new split faces */
1677         for (e = 0, q = 0; e < supportSize; ++e) {
1678           PetscInt val, face;
1679 
1680           PetscCall(DMLabelGetValue(label, support[e], &val));
1681           if (val == dim - 1) {
1682             PetscCall(PetscFindInt(support[e], numSplitPoints[dep + 1], splitPoints[dep + 1], &face));
1683             PetscCheck(face >= 0, comm, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " is not a split face", support[e]);
1684             supportNew[q++] = face + pMaxNew[dep + 1];
1685           } else if (val == -(shift + dim - 1)) {
1686             supportNew[q++] = DMPlexShiftPoint_Internal(support[e], depth, depthShift) /*support[e] + depthOffset[dep+1]*/;
1687           }
1688         }
1689         supportNew[q++] = p + pMaxNew[dep + 1] + numSplitPoints[dep + 1];
1690         PetscCall(DMPlexSetSupport(sdm, splitp, supportNew));
1691         /* Hybrid face */
1692         coneNew[0] = newp;
1693         coneNew[1] = splitp;
1694         for (v = 0; v < coneSize; ++v) {
1695           PetscInt vertex;
1696           PetscCall(PetscFindInt(cone[v], numSplitPoints[dep - 1], splitPoints[dep - 1], &vertex));
1697           if (vertex < 0) {
1698             PetscCall(PetscFindInt(cone[v], numUnsplitPoints[dep - 1], unsplitPoints[dep - 1], &vertex));
1699             PetscCheck(vertex >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Could not locate point %" PetscInt_FMT " in split or unsplit points of depth %" PetscInt_FMT, cone[v], dep - 1);
1700             coneNew[2 + v] = vertex + pMaxNew[dep] + numSplitPoints[dep] + numSplitPoints[dep - 1];
1701           } else {
1702             coneNew[2 + v] = vertex + pMaxNew[dep] + numSplitPoints[dep];
1703           }
1704         }
1705         PetscCall(DMPlexSetCone(sdm, hybface, coneNew));
1706         for (e = 0, qf = 0; e < supportSize; ++e) {
1707           PetscInt val, face;
1708 
1709           PetscCall(DMLabelGetValue(label, support[e], &val));
1710           if (val == dim - 1) {
1711             PetscCall(PetscFindInt(support[e], numSplitPoints[dep + 1], splitPoints[dep + 1], &face));
1712             PetscCheck(face >= 0, comm, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " is not a split face", support[e]);
1713             supportNew[qf++] = face + pMaxNew[dep + 2] + numSplitPoints[dep + 2];
1714           }
1715         }
1716         PetscCall(DMPlexSetSupport(sdm, hybface, supportNew));
1717       }
1718     }
1719   }
1720   for (dep = 0; dep <= depth; ++dep) {
1721     for (p = 0; p < numUnsplitPoints[dep]; ++p) {
1722       const PetscInt  oldp = unsplitPoints[dep][p];
1723       const PetscInt  newp = DMPlexShiftPoint_Internal(oldp, depth, depthShift) /*oldp + depthOffset[dep]*/;
1724       const PetscInt *cone, *support;
1725       PetscInt        coneSize, supportSize, supportSizeNew, q, qf, e, f, s;
1726 
1727       PetscCall(DMPlexGetConeSize(dm, oldp, &coneSize));
1728       PetscCall(DMPlexGetCone(dm, oldp, &cone));
1729       PetscCall(DMPlexGetSupportSize(dm, oldp, &supportSize));
1730       PetscCall(DMPlexGetSupport(dm, oldp, &support));
1731       if (dep == 0) {
1732         const PetscInt hybedge = p + pMaxNew[dep + 1] + numSplitPoints[dep + 1] + numSplitPoints[dep];
1733 
1734         /* Unsplit vertex */
1735         PetscCall(DMPlexGetSupportSize(sdm, newp, &supportSizeNew));
1736         for (s = 0, q = 0; s < supportSize; ++s) {
1737           supportNew[q++] = DMPlexShiftPoint_Internal(support[s], depth, depthShift) /*support[s] + depthOffset[dep+1]*/;
1738           PetscCall(PetscFindInt(support[s], numSplitPoints[dep + 1], splitPoints[dep + 1], &e));
1739           if (e >= 0) supportNew[q++] = e + pMaxNew[dep + 1];
1740         }
1741         supportNew[q++] = hybedge;
1742         supportNew[q++] = hybedge;
1743         PetscCheck(q == supportSizeNew, comm, PETSC_ERR_ARG_WRONG, "Support size %" PetscInt_FMT " != %" PetscInt_FMT " for vertex %" PetscInt_FMT, q, supportSizeNew, newp);
1744         PetscCall(DMPlexSetSupport(sdm, newp, supportNew));
1745         /* Hybrid edge */
1746         coneNew[0] = newp;
1747         coneNew[1] = newp;
1748         PetscCall(DMPlexSetCone(sdm, hybedge, coneNew));
1749         for (e = 0, qf = 0; e < supportSize; ++e) {
1750           PetscInt val, edge;
1751 
1752           PetscCall(DMLabelGetValue(label, support[e], &val));
1753           if (val == 1) {
1754             PetscCall(PetscFindInt(support[e], numSplitPoints[dep + 1], splitPoints[dep + 1], &edge));
1755             PetscCheck(edge >= 0, comm, PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " is not a split edge", support[e]);
1756             supportNew[qf++] = edge + pMaxNew[dep + 2] + numSplitPoints[dep + 2];
1757           } else if (val == (shift2 + 1)) {
1758             PetscCall(PetscFindInt(support[e], numUnsplitPoints[dep + 1], unsplitPoints[dep + 1], &edge));
1759             PetscCheck(edge >= 0, comm, PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " is not a unsplit edge", support[e]);
1760             supportNew[qf++] = edge + pMaxNew[dep + 2] + numSplitPoints[dep + 2] + numSplitPoints[dep + 1];
1761           }
1762         }
1763         PetscCall(DMPlexSetSupport(sdm, hybedge, supportNew));
1764       } else if (dep == dim - 2) {
1765         const PetscInt hybface = p + pMaxNew[dep + 1] + numSplitPoints[dep + 1] + numSplitPoints[dep];
1766 
1767         /* Unsplit edge: Faces into original edge, split face, and hybrid face twice */
1768         for (f = 0, qf = 0; f < supportSize; ++f) {
1769           PetscInt val, face;
1770 
1771           PetscCall(DMLabelGetValue(label, support[f], &val));
1772           if (val == dim - 1) {
1773             PetscCall(PetscFindInt(support[f], numSplitPoints[dep + 1], splitPoints[dep + 1], &face));
1774             PetscCheck(face >= 0, comm, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " is not a split face", support[f]);
1775             supportNew[qf++] = DMPlexShiftPoint_Internal(support[f], depth, depthShift) /*support[f] + depthOffset[dep+1]*/;
1776             supportNew[qf++] = face + pMaxNew[dep + 1];
1777           } else {
1778             supportNew[qf++] = DMPlexShiftPoint_Internal(support[f], depth, depthShift) /*support[f] + depthOffset[dep+1]*/;
1779           }
1780         }
1781         supportNew[qf++] = hybface;
1782         supportNew[qf++] = hybface;
1783         PetscCall(DMPlexGetSupportSize(sdm, newp, &supportSizeNew));
1784         PetscCheck(qf == supportSizeNew, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Support size for unsplit edge %" PetscInt_FMT " is %" PetscInt_FMT " != %" PetscInt_FMT, newp, qf, supportSizeNew);
1785         PetscCall(DMPlexSetSupport(sdm, newp, supportNew));
1786         /* Add hybrid face */
1787         coneNew[0] = newp;
1788         coneNew[1] = newp;
1789         PetscCall(PetscFindInt(cone[0], numUnsplitPoints[dep - 1], unsplitPoints[dep - 1], &v));
1790         PetscCheck(v >= 0, comm, PETSC_ERR_ARG_WRONG, "Vertex %" PetscInt_FMT " is not an unsplit vertex", cone[0]);
1791         coneNew[2] = v + pMaxNew[dep] + numSplitPoints[dep] + numSplitPoints[dep - 1];
1792         PetscCall(PetscFindInt(cone[1], numUnsplitPoints[dep - 1], unsplitPoints[dep - 1], &v));
1793         PetscCheck(v >= 0, comm, PETSC_ERR_ARG_WRONG, "Vertex %" PetscInt_FMT " is not an unsplit vertex", cone[1]);
1794         coneNew[3] = v + pMaxNew[dep] + numSplitPoints[dep] + numSplitPoints[dep - 1];
1795         PetscCall(DMPlexSetCone(sdm, hybface, coneNew));
1796         for (f = 0, qf = 0; f < supportSize; ++f) {
1797           PetscInt val, face;
1798 
1799           PetscCall(DMLabelGetValue(label, support[f], &val));
1800           if (val == dim - 1) {
1801             PetscCall(PetscFindInt(support[f], numSplitPoints[dep + 1], splitPoints[dep + 1], &face));
1802             supportNew[qf++] = face + pMaxNew[dep + 2] + numSplitPoints[dep + 2];
1803           }
1804         }
1805         PetscCall(DMPlexGetSupportSize(sdm, hybface, &supportSizeNew));
1806         PetscCheck(qf == supportSizeNew, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Support size for hybrid face %" PetscInt_FMT " is %" PetscInt_FMT " != %" PetscInt_FMT, hybface, qf, supportSizeNew);
1807         PetscCall(DMPlexSetSupport(sdm, hybface, supportNew));
1808       }
1809     }
1810   }
1811   /* Step 6b: Replace split points in negative side cones */
1812   for (sp = 0; sp < numSP; ++sp) {
1813     PetscInt        dep = values[sp];
1814     IS              pIS;
1815     PetscInt        numPoints;
1816     const PetscInt *points;
1817 
1818     if (dep >= 0) continue;
1819     PetscCall(DMLabelGetStratumIS(label, dep, &pIS));
1820     if (!pIS) continue;
1821     dep = -dep - shift;
1822     PetscCall(ISGetLocalSize(pIS, &numPoints));
1823     PetscCall(ISGetIndices(pIS, &points));
1824     for (p = 0; p < numPoints; ++p) {
1825       const PetscInt  oldp = points[p];
1826       const PetscInt  newp = DMPlexShiftPoint_Internal(oldp, depth, depthShift) /*depthOffset[dep] + oldp*/;
1827       const PetscInt *cone;
1828       PetscInt        coneSize, c;
1829       /* PetscBool       replaced = PETSC_FALSE; */
1830 
1831       /* Negative edge: replace split vertex */
1832       /* Negative cell: replace split face */
1833       PetscCall(DMPlexGetConeSize(sdm, newp, &coneSize));
1834       PetscCall(DMPlexGetCone(sdm, newp, &cone));
1835       for (c = 0; c < coneSize; ++c) {
1836         const PetscInt coldp = DMPlexShiftPointInverse_Internal(cone[c], depth, depthShift);
1837         PetscInt       csplitp, cp, val;
1838 
1839         PetscCall(DMLabelGetValue(label, coldp, &val));
1840         if (val == dep - 1) {
1841           PetscCall(PetscFindInt(coldp, numSplitPoints[dep - 1], splitPoints[dep - 1], &cp));
1842           PetscCheck(cp >= 0, comm, PETSC_ERR_ARG_WRONG, "Point %" PetscInt_FMT " is not a split point of dimension %" PetscInt_FMT, oldp, dep - 1);
1843           csplitp = pMaxNew[dep - 1] + cp;
1844           PetscCall(DMPlexInsertCone(sdm, newp, c, csplitp));
1845           /* replaced = PETSC_TRUE; */
1846         }
1847       }
1848       /* Cells with only a vertex or edge on the submesh have no replacement */
1849       /* PetscCheck(replaced,comm, PETSC_ERR_ARG_WRONG, "The cone of point %d does not contain split points", oldp); */
1850     }
1851     PetscCall(ISRestoreIndices(pIS, &points));
1852     PetscCall(ISDestroy(&pIS));
1853   }
1854   PetscCall(DMPlexReorderCohesiveSupports(sdm));
1855   /* Step 7: Coordinates */
1856   PetscCall(DMPlexShiftCoordinates_Internal(dm, depthShift, sdm));
1857   PetscCall(DMGetCoordinateSection(sdm, &coordSection));
1858   PetscCall(DMGetCoordinatesLocal(sdm, &coordinates));
1859   PetscCall(VecGetArray(coordinates, &coords));
1860   for (v = 0; v < (numSplitPoints ? numSplitPoints[0] : 0); ++v) {
1861     const PetscInt newp   = DMPlexShiftPoint_Internal(splitPoints[0][v], depth, depthShift) /*depthOffset[0] + splitPoints[0][v]*/;
1862     const PetscInt splitp = pMaxNew[0] + v;
1863     PetscInt       dof, off, soff, d;
1864 
1865     PetscCall(PetscSectionGetDof(coordSection, newp, &dof));
1866     PetscCall(PetscSectionGetOffset(coordSection, newp, &off));
1867     PetscCall(PetscSectionGetOffset(coordSection, splitp, &soff));
1868     for (d = 0; d < dof; ++d) coords[soff + d] = coords[off + d];
1869   }
1870   PetscCall(VecRestoreArray(coordinates, &coords));
1871   /* Step 8: SF, if I can figure this out we can split the mesh in parallel */
1872   PetscCall(DMPlexShiftSF_Internal(dm, depthShift, sdm));
1873   /*   TODO We need to associate the ghost points with the correct replica */
1874   /* Step 9: Labels */
1875   PetscCall(DMPlexShiftLabels_Internal(dm, depthShift, sdm));
1876   PetscCall(DMPlexCreateVTKLabel_Internal(dm, PETSC_FALSE, sdm));
1877   PetscCall(DMGetNumLabels(sdm, &numLabels));
1878   for (dep = 0; dep <= depth; ++dep) {
1879     for (p = 0; p < numSplitPoints[dep]; ++p) {
1880       const PetscInt newp   = DMPlexShiftPoint_Internal(splitPoints[dep][p], depth, depthShift) /*depthOffset[dep] + splitPoints[dep][p]*/;
1881       const PetscInt splitp = pMaxNew[dep] + p;
1882       PetscInt       l;
1883 
1884       if (splitLabel) {
1885         const PetscInt val = 100 + dep;
1886 
1887         PetscCall(DMLabelSetValue(splitLabel, newp, val));
1888         PetscCall(DMLabelSetValue(splitLabel, splitp, -val));
1889       }
1890       for (l = 0; l < numLabels; ++l) {
1891         DMLabel     mlabel;
1892         const char *lname;
1893         PetscInt    val;
1894         PetscBool   isDepth;
1895 
1896         PetscCall(DMGetLabelName(sdm, l, &lname));
1897         PetscCall(PetscStrcmp(lname, "depth", &isDepth));
1898         if (isDepth) continue;
1899         PetscCall(DMGetLabel(sdm, lname, &mlabel));
1900         PetscCall(DMLabelGetValue(mlabel, newp, &val));
1901         if (val >= 0) PetscCall(DMLabelSetValue(mlabel, splitp, val));
1902       }
1903     }
1904   }
1905   for (sp = 0; sp < numSP; ++sp) {
1906     const PetscInt dep = values[sp];
1907 
1908     if ((dep < 0) || (dep > depth)) continue;
1909     if (splitIS[dep]) PetscCall(ISRestoreIndices(splitIS[dep], &splitPoints[dep]));
1910     PetscCall(ISDestroy(&splitIS[dep]));
1911     if (unsplitIS[dep]) PetscCall(ISRestoreIndices(unsplitIS[dep], &unsplitPoints[dep]));
1912     PetscCall(ISDestroy(&unsplitIS[dep]));
1913   }
1914   if (ghostIS) PetscCall(ISRestoreIndices(ghostIS, &ghostPoints));
1915   PetscCall(ISDestroy(&ghostIS));
1916   if (label) {
1917     PetscCall(ISRestoreIndices(valueIS, &values));
1918     PetscCall(ISDestroy(&valueIS));
1919   }
1920   for (d = 0; d <= depth; ++d) {
1921     PetscCall(DMPlexGetDepthStratum(sdm, d, NULL, &pEnd));
1922     pMaxNew[d] = pEnd - numHybridPoints[d] - numHybridPointsOld[d];
1923   }
1924   PetscCall(PetscFree3(coneNew, coneONew, supportNew));
1925   PetscCall(PetscFree5(depthMax, depthEnd, depthShift, pMaxNew, numHybridPointsOld));
1926   PetscCall(PetscFree7(splitIS, unsplitIS, numSplitPoints, numUnsplitPoints, numHybridPoints, splitPoints, unsplitPoints));
1927   PetscFunctionReturn(PETSC_SUCCESS);
1928 }
1929 
1930 /*@C
1931   DMPlexConstructCohesiveCells - Construct cohesive cells which split the face along an internal interface
1932 
1933   Collective
1934 
1935   Input Parameters:
1936 + dm    - The original `DM`
1937 - label - The `DMLabel` specifying the boundary faces (this could be auto-generated)
1938 
1939   Output Parameters:
1940 + splitLabel - The `DMLabel` containing the split points, or `NULL` if no output is desired
1941 - dmSplit    - The new `DM`
1942 
1943   Level: developer
1944 
1945 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexLabelCohesiveComplete()`
1946 @*/
1947 PetscErrorCode DMPlexConstructCohesiveCells(DM dm, DMLabel label, DMLabel splitLabel, DM *dmSplit)
1948 {
1949   DM       sdm;
1950   PetscInt dim;
1951 
1952   PetscFunctionBegin;
1953   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1954   PetscAssertPointer(dmSplit, 4);
1955   PetscCall(DMCreate(PetscObjectComm((PetscObject)dm), &sdm));
1956   PetscCall(DMSetType(sdm, DMPLEX));
1957   PetscCall(DMGetDimension(dm, &dim));
1958   PetscCall(DMSetDimension(sdm, dim));
1959   switch (dim) {
1960   case 2:
1961   case 3:
1962     PetscCall(DMPlexConstructCohesiveCells_Internal(dm, label, splitLabel, sdm));
1963     break;
1964   default:
1965     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cannot construct cohesive cells for dimension %" PetscInt_FMT, dim);
1966   }
1967   *dmSplit = sdm;
1968   PetscFunctionReturn(PETSC_SUCCESS);
1969 }
1970 
1971 /* Returns the side of the surface for a given cell with a face on the surface */
1972 static PetscErrorCode GetSurfaceSide_Static(DM dm, DM subdm, PetscInt numSubpoints, const PetscInt *subpoints, PetscInt cell, PetscInt face, PetscBool *pos)
1973 {
1974   const PetscInt *cone, *ornt;
1975   PetscInt        dim, coneSize, c;
1976 
1977   PetscFunctionBegin;
1978   *pos = PETSC_TRUE;
1979   PetscCall(DMGetDimension(dm, &dim));
1980   PetscCall(DMPlexGetConeSize(dm, cell, &coneSize));
1981   PetscCall(DMPlexGetCone(dm, cell, &cone));
1982   PetscCall(DMPlexGetConeOrientation(dm, cell, &ornt));
1983   for (c = 0; c < coneSize; ++c) {
1984     if (cone[c] == face) {
1985       PetscInt o = ornt[c];
1986 
1987       if (subdm) {
1988         const PetscInt *subcone, *subornt;
1989         PetscInt        subpoint, subface, subconeSize, sc;
1990 
1991         PetscCall(PetscFindInt(cell, numSubpoints, subpoints, &subpoint));
1992         PetscCall(PetscFindInt(face, numSubpoints, subpoints, &subface));
1993         PetscCall(DMPlexGetConeSize(subdm, subpoint, &subconeSize));
1994         PetscCall(DMPlexGetCone(subdm, subpoint, &subcone));
1995         PetscCall(DMPlexGetConeOrientation(subdm, subpoint, &subornt));
1996         for (sc = 0; sc < subconeSize; ++sc) {
1997           if (subcone[sc] == subface) {
1998             o = subornt[0];
1999             break;
2000           }
2001         }
2002         PetscCheck(sc < subconeSize, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Could not find subpoint %" PetscInt_FMT " (%" PetscInt_FMT ") in cone for subpoint %" PetscInt_FMT " (%" PetscInt_FMT ")", subface, face, subpoint, cell);
2003       }
2004       if (o >= 0) *pos = PETSC_TRUE;
2005       else *pos = PETSC_FALSE;
2006       break;
2007     }
2008   }
2009   PetscCheck(c != coneSize, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " in split face %" PetscInt_FMT " support does not have it in the cone", cell, face);
2010   PetscFunctionReturn(PETSC_SUCCESS);
2011 }
2012 
2013 static PetscErrorCode CheckFaultEdge_Private(DM dm, DMLabel label, PetscBool split)
2014 {
2015   IS              facePosIS, faceNegIS, dimIS;
2016   const PetscInt *points;
2017   PetscInt       *closure = NULL, *inclosure = NULL;
2018   PetscInt        dim, numPoints, shift = 100, shift2 = 200, debug = 0;
2019 
2020   PetscFunctionBegin;
2021   PetscCall(DMGetDimension(dm, &dim));
2022   // If any faces touching the fault divide cells on either side,
2023   //   either split them, or unsplit the connection
2024   PetscCall(DMLabelGetStratumIS(label, shift + dim - 1, &facePosIS));
2025   PetscCall(DMLabelGetStratumIS(label, -(shift + dim - 1), &faceNegIS));
2026   if (!facePosIS || !faceNegIS) {
2027     PetscCall(ISDestroy(&facePosIS));
2028     PetscCall(ISDestroy(&faceNegIS));
2029     PetscFunctionReturn(PETSC_SUCCESS);
2030   }
2031   PetscCall(ISExpand(facePosIS, faceNegIS, &dimIS));
2032   PetscCall(ISDestroy(&facePosIS));
2033   PetscCall(ISDestroy(&faceNegIS));
2034   PetscCall(ISGetLocalSize(dimIS, &numPoints));
2035   PetscCall(ISGetIndices(dimIS, &points));
2036   for (PetscInt p = 0; p < numPoints; ++p) {
2037     const PetscInt  point = points[p];
2038     const PetscInt *support;
2039     PetscInt        supportSize, valA, valB;
2040 
2041     PetscCall(DMPlexGetSupportSize(dm, point, &supportSize));
2042     if (supportSize != 2) continue;
2043     PetscCall(DMPlexGetSupport(dm, point, &support));
2044     PetscCall(DMLabelGetValue(label, support[0], &valA));
2045     PetscCall(DMLabelGetValue(label, support[1], &valB));
2046     if ((valA == -1) || (valB == -1)) continue;
2047     if (valA * valB > 0) continue;
2048     // Check that this face is not incident on only unsplit faces,
2049     //   meaning has at least one split face
2050     {
2051       PetscBool split = PETSC_FALSE;
2052       PetscInt  Ncl, val;
2053 
2054       PetscCall(DMPlexGetTransitiveClosure(dm, point, PETSC_TRUE, &Ncl, &closure));
2055       for (PetscInt cl = 0; cl < Ncl * 2; cl += 2) {
2056         PetscCall(DMLabelGetValue(label, closure[cl], &val));
2057         if (val >= 0 && val <= dim) {
2058           split = PETSC_TRUE;
2059           break;
2060         }
2061       }
2062       if (!split) continue;
2063     }
2064     if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, "Point %" PetscInt_FMT " is impinging (%" PetscInt_FMT ":%" PetscInt_FMT ", %" PetscInt_FMT ":%" PetscInt_FMT ")\n", point, support[0], valA, support[1], valB));
2065     if (split) {
2066       // Split the face
2067       PetscCall(DMLabelGetValue(label, point, &valA));
2068       PetscCall(DMLabelClearValue(label, point, valA));
2069       PetscCall(DMLabelSetValue(label, point, dim - 1));
2070       /* Label its closure:
2071         unmarked: label as unsplit
2072         incident: relabel as split
2073         split:    do nothing */
2074       {
2075         PetscInt closureSize, cl, dep;
2076 
2077         PetscCall(DMPlexGetTransitiveClosure(dm, point, PETSC_TRUE, &closureSize, &closure));
2078         for (cl = 0; cl < closureSize * 2; cl += 2) {
2079           PetscCall(DMLabelGetValue(label, closure[cl], &valA));
2080           if (valA == -1) { /* Mark as unsplit */
2081             PetscCall(DMPlexGetPointDepth(dm, closure[cl], &dep));
2082             PetscCall(DMLabelSetValue(label, closure[cl], shift2 + dep));
2083           } else if ((valA >= shift && valA < shift2) || (valA <= -shift && valA > -shift2)) {
2084             PetscCall(DMPlexGetPointDepth(dm, closure[cl], &dep));
2085             PetscCall(DMLabelClearValue(label, closure[cl], valA));
2086             PetscCall(DMLabelSetValue(label, closure[cl], dep));
2087           }
2088         }
2089       }
2090     } else {
2091       // Unsplit the incident faces and their closures
2092       PetscInt Ncl, dep, val;
2093 
2094       PetscCall(DMPlexGetTransitiveClosure(dm, point, PETSC_TRUE, &Ncl, &closure));
2095       for (PetscInt cl = 0; cl < Ncl * 2; cl += 2) {
2096         PetscCall(DMLabelGetValue(label, closure[cl], &val));
2097         if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Point %" PetscInt_FMT ":%" PetscInt_FMT "\n", closure[cl], val));
2098         if (val >= 0 && val <= dim) {
2099           PetscInt Nincl, inval, indep;
2100 
2101           if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Point %" PetscInt_FMT " is being unsplit\n", closure[cl]));
2102           PetscCall(DMPlexGetPointDepth(dm, closure[cl], &dep));
2103           PetscCall(DMLabelClearValue(label, closure[cl], val));
2104           PetscCall(DMLabelSetValue(label, closure[cl], shift2 + dep));
2105 
2106           PetscCall(DMPlexGetTransitiveClosure(dm, closure[cl], PETSC_TRUE, &Nincl, &inclosure));
2107           for (PetscInt incl = 0; incl < Nincl * 2; incl += 2) {
2108             PetscCall(DMLabelGetValue(label, inclosure[cl], &inval));
2109             if (inval >= 0 && inval <= dim) {
2110               if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, "    Point %" PetscInt_FMT " is being unsplit\n", inclosure[incl]));
2111               PetscCall(DMPlexGetPointDepth(dm, inclosure[incl], &indep));
2112               PetscCall(DMLabelClearValue(label, inclosure[incl], inval));
2113               PetscCall(DMLabelSetValue(label, inclosure[incl], shift2 + indep));
2114             }
2115           }
2116         }
2117       }
2118     }
2119   }
2120   PetscCall(DMPlexRestoreTransitiveClosure(dm, 0, PETSC_TRUE, NULL, &inclosure));
2121   PetscCall(DMPlexRestoreTransitiveClosure(dm, 0, PETSC_TRUE, NULL, &closure));
2122   PetscCall(ISRestoreIndices(dimIS, &points));
2123   PetscCall(ISDestroy(&dimIS));
2124   PetscFunctionReturn(PETSC_SUCCESS);
2125 }
2126 
2127 /*@
2128   DMPlexLabelCohesiveComplete - Starting with a label marking points on an internal surface, we add all other mesh pieces
2129   to complete the surface
2130 
2131   Input Parameters:
2132 + dm     - The `DM`
2133 . label  - A `DMLabel` marking the surface
2134 . blabel - A `DMLabel` marking the vertices on the boundary which will not be duplicated, or `NULL` to find them automatically
2135 . bvalue - Value of `DMLabel` marking the vertices on the boundary
2136 . flip   - Flag to flip the submesh normal and replace points on the other side
2137 . split  - Split faces impinging on the surface, rather than clamping the surface boundary
2138 - subdm  - The `DM` associated with the label, or `NULL`
2139 
2140   Output Parameter:
2141 . label - A `DMLabel` marking all surface points
2142 
2143   Level: developer
2144 
2145   Note:
2146   The vertices in blabel are called "unsplit" in the terminology from hybrid cell creation.
2147 
2148 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructCohesiveCells()`, `DMPlexLabelComplete()`
2149 @*/
2150 PetscErrorCode DMPlexLabelCohesiveComplete(DM dm, DMLabel label, DMLabel blabel, PetscInt bvalue, PetscBool flip, PetscBool split, DM subdm)
2151 {
2152   DMLabel         depthLabel;
2153   IS              dimIS, subpointIS = NULL;
2154   const PetscInt *points, *subpoints;
2155   const PetscInt  rev   = flip ? -1 : 1;
2156   PetscInt        shift = 100, shift2 = 200, shift3 = split ? 300 : 0, dim, depth, numPoints, numSubpoints, p, val;
2157 
2158   PetscFunctionBegin;
2159   PetscCall(DMPlexGetDepth(dm, &depth));
2160   PetscCall(DMGetDimension(dm, &dim));
2161   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
2162   if (subdm) {
2163     PetscCall(DMPlexGetSubpointIS(subdm, &subpointIS));
2164     if (subpointIS) {
2165       PetscCall(ISGetLocalSize(subpointIS, &numSubpoints));
2166       PetscCall(ISGetIndices(subpointIS, &subpoints));
2167     }
2168   }
2169   /* Mark cell on the fault, and its faces which touch the fault: cell orientation for face gives the side of the fault */
2170   PetscCall(DMLabelGetStratumIS(label, dim - 1, &dimIS));
2171   if (!dimIS) goto divide;
2172   PetscCall(ISGetLocalSize(dimIS, &numPoints));
2173   PetscCall(ISGetIndices(dimIS, &points));
2174   for (p = 0; p < numPoints; ++p) { /* Loop over fault faces */
2175     const PetscInt *support;
2176     PetscInt        supportSize, s;
2177 
2178     PetscCall(DMPlexGetSupportSize(dm, points[p], &supportSize));
2179 #if 0
2180     if (supportSize != 2) {
2181       const PetscInt *lp;
2182       PetscInt        Nlp, pind;
2183 
2184       /* Check that for a cell with a single support face, that face is in the SF */
2185       /*   THis check only works for the remote side. We would need root side information */
2186       PetscCall(PetscSFGetGraph(dm->sf, NULL, &Nlp, &lp, NULL));
2187       PetscCall(PetscFindInt(points[p], Nlp, lp, &pind));
2188       PetscCheck(pind >= 0,PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Split face %" PetscInt_FMT " has %" PetscInt_FMT " != 2 supports, and the face is not shared with another process", points[p], supportSize);
2189     }
2190 #endif
2191     PetscCall(DMPlexGetSupport(dm, points[p], &support));
2192     for (s = 0; s < supportSize; ++s) {
2193       const PetscInt *cone;
2194       PetscInt        coneSize, c;
2195       PetscBool       pos;
2196 
2197       PetscCall(GetSurfaceSide_Static(dm, subdm, numSubpoints, subpoints, support[s], points[p], &pos));
2198       if (pos) PetscCall(DMLabelSetValue(label, support[s], rev * (shift + dim)));
2199       else PetscCall(DMLabelSetValue(label, support[s], -rev * (shift + dim)));
2200       if (rev < 0) pos = !pos ? PETSC_TRUE : PETSC_FALSE;
2201       /* Put faces touching the fault in the label */
2202       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
2203       PetscCall(DMPlexGetCone(dm, support[s], &cone));
2204       for (c = 0; c < coneSize; ++c) {
2205         const PetscInt point = cone[c];
2206 
2207         PetscCall(DMLabelGetValue(label, point, &val));
2208         if (val == -1) {
2209           PetscInt *closure = NULL;
2210           PetscInt  closureSize, cl;
2211 
2212           PetscCall(DMPlexGetTransitiveClosure(dm, point, PETSC_TRUE, &closureSize, &closure));
2213           for (cl = 0; cl < closureSize * 2; cl += 2) {
2214             const PetscInt clp  = closure[cl];
2215             PetscInt       bval = -1;
2216 
2217             PetscCall(DMLabelGetValue(label, clp, &val));
2218             if (blabel) PetscCall(DMLabelGetValue(blabel, clp, &bval));
2219             if (val >= 0 && val < dim - 1 && bval < 0) {
2220               PetscCall(DMLabelSetValue(label, point, pos == PETSC_TRUE ? shift + dim - 1 : -(shift + dim - 1)));
2221               break;
2222             }
2223           }
2224           PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, &closureSize, &closure));
2225         }
2226       }
2227     }
2228   }
2229   PetscCall(ISRestoreIndices(dimIS, &points));
2230   PetscCall(ISDestroy(&dimIS));
2231   /* Mark boundary points as unsplit */
2232   if (blabel) {
2233     IS bdIS;
2234 
2235     PetscCall(DMLabelGetStratumIS(blabel, bvalue, &bdIS));
2236     PetscCall(ISGetLocalSize(bdIS, &numPoints));
2237     PetscCall(ISGetIndices(bdIS, &points));
2238     for (p = 0; p < numPoints; ++p) {
2239       const PetscInt point = points[p];
2240       PetscInt       val, bval;
2241 
2242       PetscCall(DMLabelGetValue(blabel, point, &bval));
2243       if (bval >= 0) {
2244         PetscCall(DMLabelGetValue(label, point, &val));
2245         if ((val < 0) || (val > dim)) {
2246           /* This could be a point added from splitting a vertex on an adjacent fault, otherwise its just wrong */
2247           PetscCall(DMLabelClearValue(blabel, point, bval));
2248         }
2249       }
2250     }
2251     for (p = 0; p < numPoints; ++p) {
2252       const PetscInt point = points[p];
2253       PetscInt       val, bval;
2254 
2255       PetscCall(DMLabelGetValue(blabel, point, &bval));
2256       if (bval >= 0) {
2257         const PetscInt *cone, *support;
2258         PetscInt        coneSize, supportSize, s, valA, valB, valE;
2259 
2260         /* Mark as unsplit */
2261         PetscCall(DMLabelGetValue(label, point, &val));
2262         PetscCheck(val >= 0 && val <= dim, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " has label value %" PetscInt_FMT ", should be part of the fault", point, val);
2263         PetscCall(DMLabelClearValue(label, point, val));
2264         PetscCall(DMLabelSetValue(label, point, shift2 + val));
2265         /* Check for cross-edge
2266              A cross-edge has endpoints which are both on the boundary of the surface, but the edge itself is not. */
2267         if (val != 0) continue;
2268         PetscCall(DMPlexGetSupport(dm, point, &support));
2269         PetscCall(DMPlexGetSupportSize(dm, point, &supportSize));
2270         for (s = 0; s < supportSize; ++s) {
2271           PetscCall(DMPlexGetCone(dm, support[s], &cone));
2272           PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
2273           PetscCheck(coneSize == 2, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Edge %" PetscInt_FMT " has %" PetscInt_FMT " vertices != 2", support[s], coneSize);
2274           PetscCall(DMLabelGetValue(blabel, cone[0], &valA));
2275           PetscCall(DMLabelGetValue(blabel, cone[1], &valB));
2276           PetscCall(DMLabelGetValue(blabel, support[s], &valE));
2277           if (valE < 0 && valA >= 0 && valB >= 0 && cone[0] != cone[1]) PetscCall(DMLabelSetValue(blabel, support[s], 2));
2278         }
2279       }
2280     }
2281     PetscCall(ISRestoreIndices(bdIS, &points));
2282     PetscCall(ISDestroy(&bdIS));
2283   }
2284   /* Mark ghost fault cells */
2285   {
2286     PetscSF         sf;
2287     const PetscInt *leaves;
2288     PetscInt        Nl, l;
2289 
2290     PetscCall(DMGetPointSF(dm, &sf));
2291     PetscCall(PetscSFGetGraph(sf, NULL, &Nl, &leaves, NULL));
2292     PetscCall(DMLabelGetStratumIS(label, dim - 1, &dimIS));
2293     if (!dimIS) goto divide;
2294     PetscCall(ISGetLocalSize(dimIS, &numPoints));
2295     PetscCall(ISGetIndices(dimIS, &points));
2296     if (Nl > 0) {
2297       for (p = 0; p < numPoints; ++p) {
2298         const PetscInt point = points[p];
2299         PetscInt       val;
2300 
2301         PetscCall(PetscFindInt(point, Nl, leaves, &l));
2302         if (l >= 0) {
2303           PetscInt *closure = NULL;
2304           PetscInt  closureSize, cl;
2305 
2306           PetscCall(DMLabelGetValue(label, point, &val));
2307           PetscCheck((val == dim - 1) || (val == shift2 + dim - 1), PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " has label value %" PetscInt_FMT ", should be a fault face", point, val);
2308           PetscCall(DMLabelClearValue(label, point, val));
2309           PetscCall(DMLabelSetValue(label, point, shift3 + val));
2310           PetscCall(DMPlexGetTransitiveClosure(dm, point, PETSC_TRUE, &closureSize, &closure));
2311           for (cl = 2; cl < closureSize * 2; cl += 2) {
2312             const PetscInt clp = closure[cl];
2313 
2314             PetscCall(DMLabelGetValue(label, clp, &val));
2315             PetscCheck(val != -1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " is missing from label, but is in the closure of a fault face", point);
2316             PetscCall(DMLabelClearValue(label, clp, val));
2317             PetscCall(DMLabelSetValue(label, clp, shift3 + val));
2318           }
2319           PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, &closureSize, &closure));
2320         }
2321       }
2322     }
2323     PetscCall(ISRestoreIndices(dimIS, &points));
2324     PetscCall(ISDestroy(&dimIS));
2325   }
2326 divide:
2327   if (subpointIS) PetscCall(ISRestoreIndices(subpointIS, &subpoints));
2328   PetscCall(DMPlexLabelFaultHalo(dm, label));
2329   PetscCall(CheckFaultEdge_Private(dm, label, split));
2330   PetscFunctionReturn(PETSC_SUCCESS);
2331 }
2332 
2333 /* Check that no cell have all vertices on the fault */
2334 static PetscErrorCode DMPlexCheckValidSubmesh_Private(DM dm, DMLabel label, DM subdm)
2335 {
2336   IS              subpointIS;
2337   const PetscInt *dmpoints;
2338   PetscInt        defaultValue, cStart, cEnd, c, vStart, vEnd;
2339 
2340   PetscFunctionBegin;
2341   if (!label) PetscFunctionReturn(PETSC_SUCCESS);
2342   PetscCall(DMLabelGetDefaultValue(label, &defaultValue));
2343   PetscCall(DMPlexGetSubpointIS(subdm, &subpointIS));
2344   if (!subpointIS) PetscFunctionReturn(PETSC_SUCCESS);
2345   PetscCall(DMPlexGetHeightStratum(subdm, 0, &cStart, &cEnd));
2346   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
2347   PetscCall(ISGetIndices(subpointIS, &dmpoints));
2348   for (c = cStart; c < cEnd; ++c) {
2349     PetscBool invalidCell = PETSC_TRUE;
2350     PetscInt *closure     = NULL;
2351     PetscInt  closureSize, cl;
2352 
2353     PetscCall(DMPlexGetTransitiveClosure(dm, dmpoints[c], PETSC_TRUE, &closureSize, &closure));
2354     for (cl = 0; cl < closureSize * 2; cl += 2) {
2355       PetscInt value = 0;
2356 
2357       if ((closure[cl] < vStart) || (closure[cl] >= vEnd)) continue;
2358       PetscCall(DMLabelGetValue(label, closure[cl], &value));
2359       if (value == defaultValue) {
2360         invalidCell = PETSC_FALSE;
2361         break;
2362       }
2363     }
2364     PetscCall(DMPlexRestoreTransitiveClosure(dm, dmpoints[c], PETSC_TRUE, &closureSize, &closure));
2365     if (invalidCell) {
2366       PetscCall(ISRestoreIndices(subpointIS, &dmpoints));
2367       PetscCall(ISDestroy(&subpointIS));
2368       PetscCall(DMDestroy(&subdm));
2369       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Ambiguous submesh. Cell %" PetscInt_FMT " has all of its vertices on the submesh.", dmpoints[c]);
2370     }
2371   }
2372   PetscCall(ISRestoreIndices(subpointIS, &dmpoints));
2373   PetscFunctionReturn(PETSC_SUCCESS);
2374 }
2375 
2376 /*@
2377   DMPlexCreateHybridMesh - Create a mesh with hybrid cells along an internal interface
2378 
2379   Collective
2380 
2381   Input Parameters:
2382 + dm      - The original `DM`
2383 . label   - The label specifying the interface vertices
2384 . bdlabel - The optional label specifying the interface boundary vertices
2385 - bdvalue - Value of optional label specifying the interface boundary vertices
2386 
2387   Output Parameters:
2388 + hybridLabel - The label fully marking the interface, or `NULL` if no output is desired
2389 . splitLabel  - The label containing the split points, or `NULL` if no output is desired
2390 . dmInterface - The new interface `DM`, or `NULL`
2391 - dmHybrid    - The new `DM` with cohesive cells
2392 
2393   Level: developer
2394 
2395   Note:
2396   The hybridLabel indicates what parts of the original mesh impinged on the division surface. For points
2397   directly on the division surface, they are labeled with their dimension, so an edge 7 on the division surface would be
2398   7 (1) in hybridLabel. For points that impinge from the positive side, they are labeled with 100+dim, so an edge 6 with
2399   one vertex 3 on the surface would be 6 (101) and 3 (0) in hybridLabel. If an edge 9 from the negative side of the
2400   surface also hits vertex 3, it would be 9 (-101) in hybridLabel.
2401 
2402   The splitLabel indicates what points in the new hybrid mesh were the result of splitting points in the original
2403   mesh. The label value is $\pm 100+dim$ for each point. For example, if two edges 10 and 14 in the hybrid resulting from
2404   splitting an edge in the original mesh, you would have 10 (101) and 14 (-101) in the splitLabel.
2405 
2406   The dmInterface is a `DM` built from the original division surface. It has a label which can be retrieved using
2407   `DMPlexGetSubpointMap()` which maps each point back to the point in the surface of the original mesh.
2408 
2409 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructCohesiveCells()`, `DMPlexLabelCohesiveComplete()`, `DMPlexGetSubpointMap()`, `DMCreate()`
2410 @*/
2411 PetscErrorCode DMPlexCreateHybridMesh(DM dm, DMLabel label, DMLabel bdlabel, PetscInt bdvalue, DMLabel *hybridLabel, DMLabel *splitLabel, DM *dmInterface, DM *dmHybrid)
2412 {
2413   DM       idm;
2414   DMLabel  subpointMap, hlabel, slabel = NULL;
2415   PetscInt dim;
2416 
2417   PetscFunctionBegin;
2418   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2419   if (label) PetscAssertPointer(label, 2);
2420   if (bdlabel) PetscAssertPointer(bdlabel, 3);
2421   if (hybridLabel) PetscAssertPointer(hybridLabel, 5);
2422   if (splitLabel) PetscAssertPointer(splitLabel, 6);
2423   if (dmInterface) PetscAssertPointer(dmInterface, 7);
2424   PetscAssertPointer(dmHybrid, 8);
2425   PetscCall(DMGetDimension(dm, &dim));
2426   PetscCall(DMPlexCreateSubmesh(dm, label, 1, PETSC_FALSE, &idm));
2427   PetscCall(DMPlexCheckValidSubmesh_Private(dm, label, idm));
2428   PetscCall(DMPlexOrient(idm));
2429   PetscCall(DMPlexGetSubpointMap(idm, &subpointMap));
2430   PetscCall(DMLabelDuplicate(subpointMap, &hlabel));
2431   PetscCall(DMLabelClearStratum(hlabel, dim));
2432   if (splitLabel) {
2433     const char *name;
2434     char        sname[PETSC_MAX_PATH_LEN];
2435 
2436     PetscCall(PetscObjectGetName((PetscObject)hlabel, &name));
2437     PetscCall(PetscStrncpy(sname, name, sizeof(sname)));
2438     PetscCall(PetscStrlcat(sname, " split", sizeof(sname)));
2439     PetscCall(DMLabelCreate(PETSC_COMM_SELF, sname, &slabel));
2440   }
2441   PetscCall(DMPlexLabelCohesiveComplete(dm, hlabel, bdlabel, bdvalue, PETSC_FALSE, PETSC_TRUE, idm));
2442   if (dmInterface) {
2443     *dmInterface = idm;
2444   } else PetscCall(DMDestroy(&idm));
2445   PetscCall(DMPlexConstructCohesiveCells(dm, hlabel, slabel, dmHybrid));
2446   PetscCall(DMPlexCopy_Internal(dm, PETSC_TRUE, PETSC_TRUE, *dmHybrid));
2447   if (hybridLabel) *hybridLabel = hlabel;
2448   else PetscCall(DMLabelDestroy(&hlabel));
2449   if (splitLabel) *splitLabel = slabel;
2450   {
2451     DM      cdm;
2452     DMLabel ctLabel;
2453 
2454     /* We need to somehow share the celltype label with the coordinate dm */
2455     PetscCall(DMGetCoordinateDM(*dmHybrid, &cdm));
2456     PetscCall(DMPlexGetCellTypeLabel(*dmHybrid, &ctLabel));
2457     PetscCall(DMSetLabel(cdm, ctLabel));
2458   }
2459   PetscFunctionReturn(PETSC_SUCCESS);
2460 }
2461 
2462 /* Here we need the explicit assumption that:
2463 
2464      For any marked cell, the marked vertices constitute a single face
2465 */
2466 static PetscErrorCode DMPlexMarkSubmesh_Uninterpolated(DM dm, DMLabel vertexLabel, PetscInt value, DMLabel subpointMap, PetscInt *numFaces, PetscInt *nFV, DM subdm)
2467 {
2468   IS              subvertexIS = NULL;
2469   const PetscInt *subvertices;
2470   PetscInt       *pStart, *pEnd, pSize;
2471   PetscInt        depth, dim, d, numSubVerticesInitial = 0, v;
2472 
2473   PetscFunctionBegin;
2474   *numFaces = 0;
2475   *nFV      = 0;
2476   PetscCall(DMPlexGetDepth(dm, &depth));
2477   PetscCall(DMGetDimension(dm, &dim));
2478   pSize = PetscMax(depth, dim) + 1;
2479   PetscCall(PetscMalloc2(pSize, &pStart, pSize, &pEnd));
2480   for (d = 0; d <= depth; ++d) PetscCall(DMPlexGetSimplexOrBoxCells(dm, depth - d, &pStart[d], &pEnd[d]));
2481   /* Loop over initial vertices and mark all faces in the collective star() */
2482   if (vertexLabel) PetscCall(DMLabelGetStratumIS(vertexLabel, value, &subvertexIS));
2483   if (subvertexIS) {
2484     PetscCall(ISGetSize(subvertexIS, &numSubVerticesInitial));
2485     PetscCall(ISGetIndices(subvertexIS, &subvertices));
2486   }
2487   for (v = 0; v < numSubVerticesInitial; ++v) {
2488     const PetscInt vertex = subvertices[v];
2489     PetscInt      *star   = NULL;
2490     PetscInt       starSize, s, numCells = 0, c;
2491 
2492     PetscCall(DMPlexGetTransitiveClosure(dm, vertex, PETSC_FALSE, &starSize, &star));
2493     for (s = 0; s < starSize * 2; s += 2) {
2494       const PetscInt point = star[s];
2495       if (point >= pStart[depth] && point < pEnd[depth]) star[numCells++] = point;
2496     }
2497     for (c = 0; c < numCells; ++c) {
2498       const PetscInt cell    = star[c];
2499       PetscInt      *closure = NULL;
2500       PetscInt       closureSize, cl;
2501       PetscInt       cellLoc, numCorners = 0, faceSize = 0;
2502 
2503       PetscCall(DMLabelGetValue(subpointMap, cell, &cellLoc));
2504       if (cellLoc == 2) continue;
2505       PetscCheck(cellLoc < 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Cell %" PetscInt_FMT " has dimension %" PetscInt_FMT " in the surface label", cell, cellLoc);
2506       PetscCall(DMPlexGetTransitiveClosure(dm, cell, PETSC_TRUE, &closureSize, &closure));
2507       for (cl = 0; cl < closureSize * 2; cl += 2) {
2508         const PetscInt point = closure[cl];
2509         PetscInt       vertexLoc;
2510 
2511         if (point >= pStart[0] && point < pEnd[0]) {
2512           ++numCorners;
2513           PetscCall(DMLabelGetValue(vertexLabel, point, &vertexLoc));
2514           if (vertexLoc == value) closure[faceSize++] = point;
2515         }
2516       }
2517       if (!*nFV) PetscCall(DMPlexGetNumFaceVertices(dm, dim, numCorners, nFV));
2518       PetscCheck(faceSize <= *nFV, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Invalid submesh: Too many vertices %" PetscInt_FMT " of an element on the surface", faceSize);
2519       if (faceSize == *nFV) {
2520         const PetscInt *cells = NULL;
2521         PetscInt        numCells, nc;
2522 
2523         ++(*numFaces);
2524         for (cl = 0; cl < faceSize; ++cl) PetscCall(DMLabelSetValue(subpointMap, closure[cl], 0));
2525         PetscCall(DMPlexGetJoin(dm, faceSize, closure, &numCells, &cells));
2526         for (nc = 0; nc < numCells; ++nc) PetscCall(DMLabelSetValue(subpointMap, cells[nc], 2));
2527         PetscCall(DMPlexRestoreJoin(dm, faceSize, closure, &numCells, &cells));
2528       }
2529       PetscCall(DMPlexRestoreTransitiveClosure(dm, cell, PETSC_TRUE, &closureSize, &closure));
2530     }
2531     PetscCall(DMPlexRestoreTransitiveClosure(dm, vertex, PETSC_FALSE, &starSize, &star));
2532   }
2533   if (subvertexIS) PetscCall(ISRestoreIndices(subvertexIS, &subvertices));
2534   PetscCall(ISDestroy(&subvertexIS));
2535   PetscCall(PetscFree2(pStart, pEnd));
2536   PetscFunctionReturn(PETSC_SUCCESS);
2537 }
2538 
2539 PetscErrorCode DMPlexMarkSubmesh_Interpolated(DM dm, DMLabel vertexLabel, PetscInt value, PetscBool markedFaces, PetscBool addCells, DMLabel subpointMap, DM subdm)
2540 {
2541   IS              subvertexIS = NULL;
2542   const PetscInt *subvertices;
2543   PetscInt       *pStart, *pEnd;
2544   PetscInt        dim, d, numSubVerticesInitial = 0, v;
2545 
2546   PetscFunctionBegin;
2547   PetscCall(DMGetDimension(dm, &dim));
2548   PetscCall(PetscMalloc2(dim + 1, &pStart, dim + 1, &pEnd));
2549   for (d = 0; d <= dim; ++d) PetscCall(DMPlexGetSimplexOrBoxCells(dm, dim - d, &pStart[d], &pEnd[d]));
2550   /* Loop over initial vertices and mark all faces in the collective star() */
2551   if (vertexLabel) {
2552     PetscCall(DMLabelGetStratumIS(vertexLabel, value, &subvertexIS));
2553     if (subvertexIS) {
2554       PetscCall(ISGetSize(subvertexIS, &numSubVerticesInitial));
2555       PetscCall(ISGetIndices(subvertexIS, &subvertices));
2556     }
2557   }
2558   for (v = 0; v < numSubVerticesInitial; ++v) {
2559     const PetscInt vertex = subvertices[v];
2560     PetscInt      *star   = NULL;
2561     PetscInt       starSize, s, numFaces = 0, f;
2562 
2563     PetscCall(DMPlexGetTransitiveClosure(dm, vertex, PETSC_FALSE, &starSize, &star));
2564     for (s = 0; s < starSize * 2; s += 2) {
2565       const PetscInt point = star[s];
2566       PetscInt       faceLoc;
2567 
2568       if (point >= pStart[dim - 1] && point < pEnd[dim - 1]) {
2569         if (markedFaces) {
2570           PetscCall(DMLabelGetValue(vertexLabel, point, &faceLoc));
2571           if (faceLoc < 0) continue;
2572         }
2573         star[numFaces++] = point;
2574       }
2575     }
2576     for (f = 0; f < numFaces; ++f) {
2577       const PetscInt face    = star[f];
2578       PetscInt      *closure = NULL;
2579       PetscInt       closureSize, c;
2580       PetscInt       faceLoc;
2581 
2582       PetscCall(DMLabelGetValue(subpointMap, face, &faceLoc));
2583       if (faceLoc == dim - 1) continue;
2584       PetscCheck(faceLoc < 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Face %" PetscInt_FMT " has dimension %" PetscInt_FMT " in the surface label", face, faceLoc);
2585       PetscCall(DMPlexGetTransitiveClosure(dm, face, PETSC_TRUE, &closureSize, &closure));
2586       for (c = 0; c < closureSize * 2; c += 2) {
2587         const PetscInt point = closure[c];
2588         PetscInt       vertexLoc;
2589 
2590         if (point >= pStart[0] && point < pEnd[0]) {
2591           PetscCall(DMLabelGetValue(vertexLabel, point, &vertexLoc));
2592           if (vertexLoc != value) break;
2593         }
2594       }
2595       if (c == closureSize * 2) {
2596         const PetscInt *support;
2597         PetscInt        supportSize, s;
2598 
2599         for (c = 0; c < closureSize * 2; c += 2) {
2600           const PetscInt point = closure[c];
2601 
2602           for (d = 0; d < dim; ++d) {
2603             if (point >= pStart[d] && point < pEnd[d]) {
2604               PetscCall(DMLabelSetValue(subpointMap, point, d));
2605               break;
2606             }
2607           }
2608         }
2609         PetscCall(DMPlexGetSupportSize(dm, face, &supportSize));
2610         PetscCall(DMPlexGetSupport(dm, face, &support));
2611         if (addCells)
2612           for (s = 0; s < supportSize; ++s) PetscCall(DMLabelSetValue(subpointMap, support[s], dim));
2613       }
2614       PetscCall(DMPlexRestoreTransitiveClosure(dm, face, PETSC_TRUE, &closureSize, &closure));
2615     }
2616     PetscCall(DMPlexRestoreTransitiveClosure(dm, vertex, PETSC_FALSE, &starSize, &star));
2617   }
2618   if (subvertexIS) PetscCall(ISRestoreIndices(subvertexIS, &subvertices));
2619   PetscCall(ISDestroy(&subvertexIS));
2620   PetscCall(PetscFree2(pStart, pEnd));
2621   PetscFunctionReturn(PETSC_SUCCESS);
2622 }
2623 
2624 static PetscErrorCode DMPlexMarkCohesiveSubmesh_Uninterpolated(DM dm, PetscBool hasLagrange, const char labelname[], PetscInt value, DMLabel subpointMap, PetscInt *numFaces, PetscInt *nFV, PetscInt *subCells[], DM subdm)
2625 {
2626   DMLabel         label = NULL;
2627   const PetscInt *cone;
2628   PetscInt        dim, cMax, cEnd, c, subc = 0, p, coneSize = -1;
2629 
2630   PetscFunctionBegin;
2631   *numFaces = 0;
2632   *nFV      = 0;
2633   if (labelname) PetscCall(DMGetLabel(dm, labelname, &label));
2634   *subCells = NULL;
2635   PetscCall(DMGetDimension(dm, &dim));
2636   PetscCall(DMPlexGetTensorPrismBounds_Internal(dm, dim, &cMax, &cEnd));
2637   if (cMax < 0) PetscFunctionReturn(PETSC_SUCCESS);
2638   if (label) {
2639     for (c = cMax; c < cEnd; ++c) {
2640       PetscInt val;
2641 
2642       PetscCall(DMLabelGetValue(label, c, &val));
2643       if (val == value) {
2644         ++(*numFaces);
2645         PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
2646       }
2647     }
2648   } else {
2649     *numFaces = cEnd - cMax;
2650     PetscCall(DMPlexGetConeSize(dm, cMax, &coneSize));
2651   }
2652   PetscCall(PetscMalloc1(*numFaces * 2, subCells));
2653   if (!*numFaces) PetscFunctionReturn(PETSC_SUCCESS);
2654   *nFV = hasLagrange ? coneSize / 3 : coneSize / 2;
2655   for (c = cMax; c < cEnd; ++c) {
2656     const PetscInt *cells;
2657     PetscInt        numCells;
2658 
2659     if (label) {
2660       PetscInt val;
2661 
2662       PetscCall(DMLabelGetValue(label, c, &val));
2663       if (val != value) continue;
2664     }
2665     PetscCall(DMPlexGetCone(dm, c, &cone));
2666     for (p = 0; p < *nFV; ++p) PetscCall(DMLabelSetValue(subpointMap, cone[p], 0));
2667     /* Negative face */
2668     PetscCall(DMPlexGetJoin(dm, *nFV, cone, &numCells, &cells));
2669     /* Not true in parallel
2670     PetscCheck(numCells == 2,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cohesive cells should separate two cells"); */
2671     for (p = 0; p < numCells; ++p) {
2672       PetscCall(DMLabelSetValue(subpointMap, cells[p], 2));
2673       (*subCells)[subc++] = cells[p];
2674     }
2675     PetscCall(DMPlexRestoreJoin(dm, *nFV, cone, &numCells, &cells));
2676     /* Positive face is not included */
2677   }
2678   PetscFunctionReturn(PETSC_SUCCESS);
2679 }
2680 
2681 static PetscErrorCode DMPlexMarkCohesiveSubmesh_Interpolated(DM dm, DMLabel label, PetscInt value, DMLabel subpointMap, DM subdm)
2682 {
2683   PetscInt *pStart, *pEnd;
2684   PetscInt  dim, cMax, cEnd, c, d;
2685 
2686   PetscFunctionBegin;
2687   PetscCall(DMGetDimension(dm, &dim));
2688   PetscCall(DMPlexGetTensorPrismBounds_Internal(dm, dim, &cMax, &cEnd));
2689   if (cMax < 0) PetscFunctionReturn(PETSC_SUCCESS);
2690   PetscCall(PetscMalloc2(dim + 1, &pStart, dim + 1, &pEnd));
2691   for (d = 0; d <= dim; ++d) PetscCall(DMPlexGetDepthStratum(dm, d, &pStart[d], &pEnd[d]));
2692   for (c = cMax; c < cEnd; ++c) {
2693     const PetscInt *cone;
2694     PetscInt       *closure = NULL;
2695     PetscInt        fconeSize, coneSize, closureSize, cl, val;
2696 
2697     if (label) {
2698       PetscCall(DMLabelGetValue(label, c, &val));
2699       if (val != value) continue;
2700     }
2701     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
2702     PetscCall(DMPlexGetCone(dm, c, &cone));
2703     PetscCall(DMPlexGetConeSize(dm, cone[0], &fconeSize));
2704     PetscCheck(coneSize == (fconeSize ? fconeSize : 1) + 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cohesive cells should separate two cells");
2705     /* Negative face */
2706     PetscCall(DMPlexGetTransitiveClosure(dm, cone[0], PETSC_TRUE, &closureSize, &closure));
2707     for (cl = 0; cl < closureSize * 2; cl += 2) {
2708       const PetscInt point = closure[cl];
2709 
2710       for (d = 0; d <= dim; ++d) {
2711         if (point >= pStart[d] && point < pEnd[d]) {
2712           PetscCall(DMLabelSetValue(subpointMap, point, d));
2713           break;
2714         }
2715       }
2716     }
2717     PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], PETSC_TRUE, &closureSize, &closure));
2718     /* Cells -- positive face is not included */
2719     for (cl = 0; cl < 1; ++cl) {
2720       const PetscInt *support;
2721       PetscInt        supportSize, s;
2722 
2723       PetscCall(DMPlexGetSupportSize(dm, cone[cl], &supportSize));
2724       /* PetscCheck(supportSize == 2,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cohesive faces should separate two cells"); */
2725       PetscCall(DMPlexGetSupport(dm, cone[cl], &support));
2726       for (s = 0; s < supportSize; ++s) PetscCall(DMLabelSetValue(subpointMap, support[s], dim));
2727     }
2728   }
2729   PetscCall(PetscFree2(pStart, pEnd));
2730   PetscFunctionReturn(PETSC_SUCCESS);
2731 }
2732 
2733 static PetscErrorCode DMPlexGetFaceOrientation(DM dm, PetscInt cell, PetscInt numCorners, PetscInt indices[], PetscInt oppositeVertex, PetscInt origVertices[], PetscInt faceVertices[], PetscBool *posOriented)
2734 {
2735   MPI_Comm       comm;
2736   PetscBool      posOrient = PETSC_FALSE;
2737   const PetscInt debug     = 0;
2738   PetscInt       cellDim, faceSize, f;
2739 
2740   PetscFunctionBegin;
2741   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
2742   PetscCall(DMGetDimension(dm, &cellDim));
2743   if (debug) PetscCall(PetscPrintf(comm, "cellDim: %" PetscInt_FMT " numCorners: %" PetscInt_FMT "\n", cellDim, numCorners));
2744 
2745   if (cellDim == 1 && numCorners == 2) {
2746     /* Triangle */
2747     faceSize  = numCorners - 1;
2748     posOrient = !(oppositeVertex % 2) ? PETSC_TRUE : PETSC_FALSE;
2749   } else if (cellDim == 2 && numCorners == 3) {
2750     /* Triangle */
2751     faceSize  = numCorners - 1;
2752     posOrient = !(oppositeVertex % 2) ? PETSC_TRUE : PETSC_FALSE;
2753   } else if (cellDim == 3 && numCorners == 4) {
2754     /* Tetrahedron */
2755     faceSize  = numCorners - 1;
2756     posOrient = (oppositeVertex % 2) ? PETSC_TRUE : PETSC_FALSE;
2757   } else if (cellDim == 1 && numCorners == 3) {
2758     /* Quadratic line */
2759     faceSize  = 1;
2760     posOrient = PETSC_TRUE;
2761   } else if (cellDim == 2 && numCorners == 4) {
2762     /* Quads */
2763     faceSize = 2;
2764     if (indices[1] > indices[0] && indices[1] - indices[0] == 1) {
2765       posOrient = PETSC_TRUE;
2766     } else if (indices[0] == 3 && indices[1] == 0) {
2767       posOrient = PETSC_TRUE;
2768     } else {
2769       PetscCheck((indices[0] > indices[1] && indices[0] - indices[1] == 1) || (indices[0] == 0 && indices[1] == 3), comm, PETSC_ERR_ARG_WRONG, "Invalid quad crossedge");
2770       posOrient = PETSC_FALSE;
2771     }
2772   } else if (cellDim == 2 && numCorners == 6) {
2773     /* Quadratic triangle (I hate this) */
2774     /* Edges are determined by the first 2 vertices (corners of edges) */
2775     const PetscInt faceSizeTri = 3;
2776     PetscInt       sortedIndices[3], i, iFace;
2777     PetscBool      found                    = PETSC_FALSE;
2778     PetscInt       faceVerticesTriSorted[9] = {
2779       0, 3, 4, /* bottom */
2780       1, 4, 5, /* right */
2781       2, 3, 5, /* left */
2782     };
2783     PetscInt faceVerticesTri[9] = {
2784       0, 3, 4, /* bottom */
2785       1, 4, 5, /* right */
2786       2, 5, 3, /* left */
2787     };
2788 
2789     for (i = 0; i < faceSizeTri; ++i) sortedIndices[i] = indices[i];
2790     PetscCall(PetscSortInt(faceSizeTri, sortedIndices));
2791     for (iFace = 0; iFace < 3; ++iFace) {
2792       const PetscInt ii = iFace * faceSizeTri;
2793       PetscInt       fVertex, cVertex;
2794 
2795       if (sortedIndices[0] == faceVerticesTriSorted[ii + 0] && sortedIndices[1] == faceVerticesTriSorted[ii + 1]) {
2796         for (fVertex = 0; fVertex < faceSizeTri; ++fVertex) {
2797           for (cVertex = 0; cVertex < faceSizeTri; ++cVertex) {
2798             if (indices[cVertex] == faceVerticesTri[ii + fVertex]) {
2799               faceVertices[fVertex] = origVertices[cVertex];
2800               break;
2801             }
2802           }
2803         }
2804         found = PETSC_TRUE;
2805         break;
2806       }
2807     }
2808     PetscCheck(found, comm, PETSC_ERR_ARG_WRONG, "Invalid tri crossface");
2809     if (posOriented) *posOriented = PETSC_TRUE;
2810     PetscFunctionReturn(PETSC_SUCCESS);
2811   } else if (cellDim == 2 && numCorners == 9) {
2812     /* Quadratic quad (I hate this) */
2813     /* Edges are determined by the first 2 vertices (corners of edges) */
2814     const PetscInt faceSizeQuad = 3;
2815     PetscInt       sortedIndices[3], i, iFace;
2816     PetscBool      found                      = PETSC_FALSE;
2817     PetscInt       faceVerticesQuadSorted[12] = {
2818       0, 1, 4, /* bottom */
2819       1, 2, 5, /* right */
2820       2, 3, 6, /* top */
2821       0, 3, 7, /* left */
2822     };
2823     PetscInt faceVerticesQuad[12] = {
2824       0, 1, 4, /* bottom */
2825       1, 2, 5, /* right */
2826       2, 3, 6, /* top */
2827       3, 0, 7, /* left */
2828     };
2829 
2830     for (i = 0; i < faceSizeQuad; ++i) sortedIndices[i] = indices[i];
2831     PetscCall(PetscSortInt(faceSizeQuad, sortedIndices));
2832     for (iFace = 0; iFace < 4; ++iFace) {
2833       const PetscInt ii = iFace * faceSizeQuad;
2834       PetscInt       fVertex, cVertex;
2835 
2836       if (sortedIndices[0] == faceVerticesQuadSorted[ii + 0] && sortedIndices[1] == faceVerticesQuadSorted[ii + 1]) {
2837         for (fVertex = 0; fVertex < faceSizeQuad; ++fVertex) {
2838           for (cVertex = 0; cVertex < faceSizeQuad; ++cVertex) {
2839             if (indices[cVertex] == faceVerticesQuad[ii + fVertex]) {
2840               faceVertices[fVertex] = origVertices[cVertex];
2841               break;
2842             }
2843           }
2844         }
2845         found = PETSC_TRUE;
2846         break;
2847       }
2848     }
2849     PetscCheck(found, comm, PETSC_ERR_ARG_WRONG, "Invalid quad crossface");
2850     if (posOriented) *posOriented = PETSC_TRUE;
2851     PetscFunctionReturn(PETSC_SUCCESS);
2852   } else if (cellDim == 3 && numCorners == 8) {
2853     /* Hexes
2854        A hex is two oriented quads with the normal of the first
2855        pointing up at the second.
2856 
2857           7---6
2858          /|  /|
2859         4---5 |
2860         | 1-|-2
2861         |/  |/
2862         0---3
2863 
2864         Faces are determined by the first 4 vertices (corners of faces) */
2865     const PetscInt faceSizeHex = 4;
2866     PetscInt       sortedIndices[4], i, iFace;
2867     PetscBool      found                     = PETSC_FALSE;
2868     PetscInt       faceVerticesHexSorted[24] = {
2869       0, 1, 2, 3, /* bottom */
2870       4, 5, 6, 7, /* top */
2871       0, 3, 4, 5, /* front */
2872       2, 3, 5, 6, /* right */
2873       1, 2, 6, 7, /* back */
2874       0, 1, 4, 7, /* left */
2875     };
2876     PetscInt faceVerticesHex[24] = {
2877       1, 2, 3, 0, /* bottom */
2878       4, 5, 6, 7, /* top */
2879       0, 3, 5, 4, /* front */
2880       3, 2, 6, 5, /* right */
2881       2, 1, 7, 6, /* back */
2882       1, 0, 4, 7, /* left */
2883     };
2884 
2885     for (i = 0; i < faceSizeHex; ++i) sortedIndices[i] = indices[i];
2886     PetscCall(PetscSortInt(faceSizeHex, sortedIndices));
2887     for (iFace = 0; iFace < 6; ++iFace) {
2888       const PetscInt ii = iFace * faceSizeHex;
2889       PetscInt       fVertex, cVertex;
2890 
2891       if (sortedIndices[0] == faceVerticesHexSorted[ii + 0] && sortedIndices[1] == faceVerticesHexSorted[ii + 1] && sortedIndices[2] == faceVerticesHexSorted[ii + 2] && sortedIndices[3] == faceVerticesHexSorted[ii + 3]) {
2892         for (fVertex = 0; fVertex < faceSizeHex; ++fVertex) {
2893           for (cVertex = 0; cVertex < faceSizeHex; ++cVertex) {
2894             if (indices[cVertex] == faceVerticesHex[ii + fVertex]) {
2895               faceVertices[fVertex] = origVertices[cVertex];
2896               break;
2897             }
2898           }
2899         }
2900         found = PETSC_TRUE;
2901         break;
2902       }
2903     }
2904     PetscCheck(found, comm, PETSC_ERR_ARG_WRONG, "Invalid hex crossface");
2905     if (posOriented) *posOriented = PETSC_TRUE;
2906     PetscFunctionReturn(PETSC_SUCCESS);
2907   } else if (cellDim == 3 && numCorners == 10) {
2908     /* Quadratic tet */
2909     /* Faces are determined by the first 3 vertices (corners of faces) */
2910     const PetscInt faceSizeTet = 6;
2911     PetscInt       sortedIndices[6], i, iFace;
2912     PetscBool      found                     = PETSC_FALSE;
2913     PetscInt       faceVerticesTetSorted[24] = {
2914       0, 1, 2, 6, 7, 8, /* bottom */
2915       0, 3, 4, 6, 7, 9, /* front */
2916       1, 4, 5, 7, 8, 9, /* right */
2917       2, 3, 5, 6, 8, 9, /* left */
2918     };
2919     PetscInt faceVerticesTet[24] = {
2920       0, 1, 2, 6, 7, 8, /* bottom */
2921       0, 4, 3, 6, 7, 9, /* front */
2922       1, 5, 4, 7, 8, 9, /* right */
2923       2, 3, 5, 8, 6, 9, /* left */
2924     };
2925 
2926     for (i = 0; i < faceSizeTet; ++i) sortedIndices[i] = indices[i];
2927     PetscCall(PetscSortInt(faceSizeTet, sortedIndices));
2928     for (iFace = 0; iFace < 4; ++iFace) {
2929       const PetscInt ii = iFace * faceSizeTet;
2930       PetscInt       fVertex, cVertex;
2931 
2932       if (sortedIndices[0] == faceVerticesTetSorted[ii + 0] && sortedIndices[1] == faceVerticesTetSorted[ii + 1] && sortedIndices[2] == faceVerticesTetSorted[ii + 2] && sortedIndices[3] == faceVerticesTetSorted[ii + 3]) {
2933         for (fVertex = 0; fVertex < faceSizeTet; ++fVertex) {
2934           for (cVertex = 0; cVertex < faceSizeTet; ++cVertex) {
2935             if (indices[cVertex] == faceVerticesTet[ii + fVertex]) {
2936               faceVertices[fVertex] = origVertices[cVertex];
2937               break;
2938             }
2939           }
2940         }
2941         found = PETSC_TRUE;
2942         break;
2943       }
2944     }
2945     PetscCheck(found, comm, PETSC_ERR_ARG_WRONG, "Invalid tet crossface");
2946     if (posOriented) *posOriented = PETSC_TRUE;
2947     PetscFunctionReturn(PETSC_SUCCESS);
2948   } else if (cellDim == 3 && numCorners == 27) {
2949     /* Quadratic hexes (I hate this)
2950        A hex is two oriented quads with the normal of the first
2951        pointing up at the second.
2952 
2953          7---6
2954         /|  /|
2955        4---5 |
2956        | 3-|-2
2957        |/  |/
2958        0---1
2959 
2960        Faces are determined by the first 4 vertices (corners of faces) */
2961     const PetscInt faceSizeQuadHex = 9;
2962     PetscInt       sortedIndices[9], i, iFace;
2963     PetscBool      found                         = PETSC_FALSE;
2964     PetscInt       faceVerticesQuadHexSorted[54] = {
2965       0, 1, 2, 3, 8,  9,  10, 11, 24, /* bottom */
2966       4, 5, 6, 7, 12, 13, 14, 15, 25, /* top */
2967       0, 1, 4, 5, 8,  12, 16, 17, 22, /* front */
2968       1, 2, 5, 6, 9,  13, 17, 18, 21, /* right */
2969       2, 3, 6, 7, 10, 14, 18, 19, 23, /* back */
2970       0, 3, 4, 7, 11, 15, 16, 19, 20, /* left */
2971     };
2972     PetscInt faceVerticesQuadHex[54] = {
2973       3, 2, 1, 0, 10, 9,  8,  11, 24, /* bottom */
2974       4, 5, 6, 7, 12, 13, 14, 15, 25, /* top */
2975       0, 1, 5, 4, 8,  17, 12, 16, 22, /* front */
2976       1, 2, 6, 5, 9,  18, 13, 17, 21, /* right */
2977       2, 3, 7, 6, 10, 19, 14, 18, 23, /* back */
2978       3, 0, 4, 7, 11, 16, 15, 19, 20  /* left */
2979     };
2980 
2981     for (i = 0; i < faceSizeQuadHex; ++i) sortedIndices[i] = indices[i];
2982     PetscCall(PetscSortInt(faceSizeQuadHex, sortedIndices));
2983     for (iFace = 0; iFace < 6; ++iFace) {
2984       const PetscInt ii = iFace * faceSizeQuadHex;
2985       PetscInt       fVertex, cVertex;
2986 
2987       if (sortedIndices[0] == faceVerticesQuadHexSorted[ii + 0] && sortedIndices[1] == faceVerticesQuadHexSorted[ii + 1] && sortedIndices[2] == faceVerticesQuadHexSorted[ii + 2] && sortedIndices[3] == faceVerticesQuadHexSorted[ii + 3]) {
2988         for (fVertex = 0; fVertex < faceSizeQuadHex; ++fVertex) {
2989           for (cVertex = 0; cVertex < faceSizeQuadHex; ++cVertex) {
2990             if (indices[cVertex] == faceVerticesQuadHex[ii + fVertex]) {
2991               faceVertices[fVertex] = origVertices[cVertex];
2992               break;
2993             }
2994           }
2995         }
2996         found = PETSC_TRUE;
2997         break;
2998       }
2999     }
3000     PetscCheck(found, comm, PETSC_ERR_ARG_WRONG, "Invalid hex crossface");
3001     if (posOriented) *posOriented = PETSC_TRUE;
3002     PetscFunctionReturn(PETSC_SUCCESS);
3003   } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "Unknown cell type for faceOrientation().");
3004   if (!posOrient) {
3005     if (debug) PetscCall(PetscPrintf(comm, "  Reversing initial face orientation\n"));
3006     for (f = 0; f < faceSize; ++f) faceVertices[f] = origVertices[faceSize - 1 - f];
3007   } else {
3008     if (debug) PetscCall(PetscPrintf(comm, "  Keeping initial face orientation\n"));
3009     for (f = 0; f < faceSize; ++f) faceVertices[f] = origVertices[f];
3010   }
3011   if (posOriented) *posOriented = posOrient;
3012   PetscFunctionReturn(PETSC_SUCCESS);
3013 }
3014 
3015 /*@
3016   DMPlexGetOrientedFace - Given a cell and a face, as a set of vertices, return the oriented face, as a set of vertices,
3017   in faceVertices. The orientation is such that the face normal points out of the cell
3018 
3019   Not Collective
3020 
3021   Input Parameters:
3022 + dm           - The original mesh
3023 . cell         - The cell mesh point
3024 . faceSize     - The number of vertices on the face
3025 . face         - The face vertices
3026 . numCorners   - The number of vertices on the cell
3027 . indices      - Local numbering of face vertices in cell cone
3028 - origVertices - Original face vertices
3029 
3030   Output Parameters:
3031 + faceVertices - The face vertices properly oriented
3032 - posOriented  - `PETSC_TRUE` if the face was oriented with outward normal
3033 
3034   Level: developer
3035 
3036 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
3037 @*/
3038 PetscErrorCode DMPlexGetOrientedFace(DM dm, PetscInt cell, PetscInt faceSize, const PetscInt face[], PetscInt numCorners, PetscInt indices[], PetscInt origVertices[], PetscInt faceVertices[], PetscBool *posOriented)
3039 {
3040   const PetscInt *cone = NULL;
3041   PetscInt        coneSize, v, f, v2;
3042   PetscInt        oppositeVertex = -1;
3043 
3044   PetscFunctionBegin;
3045   PetscCall(DMPlexGetConeSize(dm, cell, &coneSize));
3046   PetscCall(DMPlexGetCone(dm, cell, &cone));
3047   for (v = 0, v2 = 0; v < coneSize; ++v) {
3048     PetscBool found = PETSC_FALSE;
3049 
3050     for (f = 0; f < faceSize; ++f) {
3051       if (face[f] == cone[v]) {
3052         found = PETSC_TRUE;
3053         break;
3054       }
3055     }
3056     if (found) {
3057       indices[v2]      = v;
3058       origVertices[v2] = cone[v];
3059       ++v2;
3060     } else {
3061       oppositeVertex = v;
3062     }
3063   }
3064   PetscCall(DMPlexGetFaceOrientation(dm, cell, numCorners, indices, oppositeVertex, origVertices, faceVertices, posOriented));
3065   PetscFunctionReturn(PETSC_SUCCESS);
3066 }
3067 
3068 /*
3069   DMPlexInsertFace_Internal - Puts a face into the mesh
3070 
3071   Not Collective
3072 
3073   Input Parameters:
3074   + dm              - The `DMPLEX`
3075   . numFaceVertex   - The number of vertices in the face
3076   . faceVertices    - The vertices in the face for `dm`
3077   . subfaceVertices - The vertices in the face for subdm
3078   . numCorners      - The number of vertices in the `cell`
3079   . cell            - A cell in `dm` containing the face
3080   . subcell         - A cell in subdm containing the face
3081   . firstFace       - First face in the mesh
3082   - newFacePoint    - Next face in the mesh
3083 
3084   Output Parameter:
3085   . newFacePoint - Contains next face point number on input, updated on output
3086 
3087   Level: developer
3088 */
3089 static PetscErrorCode DMPlexInsertFace_Internal(DM dm, DM subdm, PetscInt numFaceVertices, const PetscInt faceVertices[], const PetscInt subfaceVertices[], PetscInt numCorners, PetscInt cell, PetscInt subcell, PetscInt firstFace, PetscInt *newFacePoint)
3090 {
3091   MPI_Comm        comm;
3092   DM_Plex        *submesh = (DM_Plex *)subdm->data;
3093   const PetscInt *faces;
3094   PetscInt        numFaces, coneSize;
3095 
3096   PetscFunctionBegin;
3097   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
3098   PetscCall(DMPlexGetConeSize(subdm, subcell, &coneSize));
3099   PetscCheck(coneSize == 1, comm, PETSC_ERR_ARG_OUTOFRANGE, "Cone size of cell %" PetscInt_FMT " is %" PetscInt_FMT " != 1", cell, coneSize);
3100 #if 0
3101   /* Cannot use this because support() has not been constructed yet */
3102   PetscCall(DMPlexGetJoin(subdm, numFaceVertices, subfaceVertices, &numFaces, &faces));
3103 #else
3104   {
3105     PetscInt f;
3106 
3107     numFaces = 0;
3108     PetscCall(DMGetWorkArray(subdm, 1, MPIU_INT, (void **)&faces));
3109     for (f = firstFace; f < *newFacePoint; ++f) {
3110       PetscInt dof, off, d;
3111 
3112       PetscCall(PetscSectionGetDof(submesh->coneSection, f, &dof));
3113       PetscCall(PetscSectionGetOffset(submesh->coneSection, f, &off));
3114       /* Yes, I know this is quadratic, but I expect the sizes to be <5 */
3115       for (d = 0; d < dof; ++d) {
3116         const PetscInt p = submesh->cones[off + d];
3117         PetscInt       v;
3118 
3119         for (v = 0; v < numFaceVertices; ++v) {
3120           if (subfaceVertices[v] == p) break;
3121         }
3122         if (v == numFaceVertices) break;
3123       }
3124       if (d == dof) {
3125         numFaces               = 1;
3126         ((PetscInt *)faces)[0] = f;
3127       }
3128     }
3129   }
3130 #endif
3131   PetscCheck(numFaces <= 1, comm, PETSC_ERR_ARG_WRONG, "Vertex set had %" PetscInt_FMT " faces, not one", numFaces);
3132   if (numFaces == 1) {
3133     /* Add the other cell neighbor for this face */
3134     PetscCall(DMPlexSetCone(subdm, subcell, faces));
3135   } else {
3136     PetscInt *indices, *origVertices, *orientedVertices, *orientedSubVertices, v, ov;
3137     PetscBool posOriented;
3138 
3139     PetscCall(DMGetWorkArray(subdm, 4 * numFaceVertices * sizeof(PetscInt), MPIU_INT, &orientedVertices));
3140     origVertices        = &orientedVertices[numFaceVertices];
3141     indices             = &orientedVertices[numFaceVertices * 2];
3142     orientedSubVertices = &orientedVertices[numFaceVertices * 3];
3143     PetscCall(DMPlexGetOrientedFace(dm, cell, numFaceVertices, faceVertices, numCorners, indices, origVertices, orientedVertices, &posOriented));
3144     /* TODO: I know that routine should return a permutation, not the indices */
3145     for (v = 0; v < numFaceVertices; ++v) {
3146       const PetscInt vertex = faceVertices[v], subvertex = subfaceVertices[v];
3147       for (ov = 0; ov < numFaceVertices; ++ov) {
3148         if (orientedVertices[ov] == vertex) {
3149           orientedSubVertices[ov] = subvertex;
3150           break;
3151         }
3152       }
3153       PetscCheck(ov != numFaceVertices, comm, PETSC_ERR_PLIB, "Could not find face vertex %" PetscInt_FMT " in orientated set", vertex);
3154     }
3155     PetscCall(DMPlexSetCone(subdm, *newFacePoint, orientedSubVertices));
3156     PetscCall(DMPlexSetCone(subdm, subcell, newFacePoint));
3157     PetscCall(DMRestoreWorkArray(subdm, 4 * numFaceVertices * sizeof(PetscInt), MPIU_INT, &orientedVertices));
3158     ++(*newFacePoint);
3159   }
3160 #if 0
3161   PetscCall(DMPlexRestoreJoin(subdm, numFaceVertices, subfaceVertices, &numFaces, &faces));
3162 #else
3163   PetscCall(DMRestoreWorkArray(subdm, 1, MPIU_INT, (void **)&faces));
3164 #endif
3165   PetscFunctionReturn(PETSC_SUCCESS);
3166 }
3167 
3168 static PetscErrorCode DMPlexCreateSubmesh_Uninterpolated(DM dm, DMLabel vertexLabel, PetscInt value, DM subdm)
3169 {
3170   MPI_Comm        comm;
3171   DMLabel         subpointMap;
3172   IS              subvertexIS, subcellIS;
3173   const PetscInt *subVertices, *subCells;
3174   PetscInt        numSubVertices, firstSubVertex, numSubCells;
3175   PetscInt       *subface, maxConeSize, numSubFaces = 0, firstSubFace, newFacePoint, nFV = 0;
3176   PetscInt        vStart, vEnd, c, f;
3177 
3178   PetscFunctionBegin;
3179   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
3180   /* Create subpointMap which marks the submesh */
3181   PetscCall(DMLabelCreate(PETSC_COMM_SELF, "subpoint_map", &subpointMap));
3182   PetscCall(DMPlexSetSubpointMap(subdm, subpointMap));
3183   if (vertexLabel) PetscCall(DMPlexMarkSubmesh_Uninterpolated(dm, vertexLabel, value, subpointMap, &numSubFaces, &nFV, subdm));
3184   /* Setup chart */
3185   PetscCall(DMLabelGetStratumSize(subpointMap, 0, &numSubVertices));
3186   PetscCall(DMLabelGetStratumSize(subpointMap, 2, &numSubCells));
3187   PetscCall(DMPlexSetChart(subdm, 0, numSubCells + numSubFaces + numSubVertices));
3188   PetscCall(DMPlexSetVTKCellHeight(subdm, 1));
3189   /* Set cone sizes */
3190   firstSubVertex = numSubCells;
3191   firstSubFace   = numSubCells + numSubVertices;
3192   newFacePoint   = firstSubFace;
3193   PetscCall(DMLabelGetStratumIS(subpointMap, 0, &subvertexIS));
3194   if (subvertexIS) PetscCall(ISGetIndices(subvertexIS, &subVertices));
3195   PetscCall(DMLabelGetStratumIS(subpointMap, 2, &subcellIS));
3196   if (subcellIS) PetscCall(ISGetIndices(subcellIS, &subCells));
3197   for (c = 0; c < numSubCells; ++c) PetscCall(DMPlexSetConeSize(subdm, c, 1));
3198   for (f = firstSubFace; f < firstSubFace + numSubFaces; ++f) PetscCall(DMPlexSetConeSize(subdm, f, nFV));
3199   PetscCall(DMSetUp(subdm));
3200   PetscCall(DMLabelDestroy(&subpointMap));
3201   /* Create face cones */
3202   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
3203   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, NULL));
3204   PetscCall(DMGetWorkArray(subdm, maxConeSize, MPIU_INT, (void **)&subface));
3205   for (c = 0; c < numSubCells; ++c) {
3206     const PetscInt cell    = subCells[c];
3207     const PetscInt subcell = c;
3208     PetscInt      *closure = NULL;
3209     PetscInt       closureSize, cl, numCorners = 0, faceSize = 0;
3210 
3211     PetscCall(DMPlexGetTransitiveClosure(dm, cell, PETSC_TRUE, &closureSize, &closure));
3212     for (cl = 0; cl < closureSize * 2; cl += 2) {
3213       const PetscInt point = closure[cl];
3214       PetscInt       subVertex;
3215 
3216       if (point >= vStart && point < vEnd) {
3217         ++numCorners;
3218         PetscCall(PetscFindInt(point, numSubVertices, subVertices, &subVertex));
3219         if (subVertex >= 0) {
3220           closure[faceSize] = point;
3221           subface[faceSize] = firstSubVertex + subVertex;
3222           ++faceSize;
3223         }
3224       }
3225     }
3226     PetscCheck(faceSize <= nFV, comm, PETSC_ERR_ARG_WRONG, "Invalid submesh: Too many vertices %" PetscInt_FMT " of an element on the surface", faceSize);
3227     if (faceSize == nFV) PetscCall(DMPlexInsertFace_Internal(dm, subdm, faceSize, closure, subface, numCorners, cell, subcell, firstSubFace, &newFacePoint));
3228     PetscCall(DMPlexRestoreTransitiveClosure(dm, cell, PETSC_TRUE, &closureSize, &closure));
3229   }
3230   PetscCall(DMRestoreWorkArray(subdm, maxConeSize, MPIU_INT, (void **)&subface));
3231   PetscCall(DMPlexSymmetrize(subdm));
3232   PetscCall(DMPlexStratify(subdm));
3233   /* Build coordinates */
3234   {
3235     PetscSection coordSection, subCoordSection;
3236     Vec          coordinates, subCoordinates;
3237     PetscScalar *coords, *subCoords;
3238     PetscInt     numComp, coordSize, v;
3239     const char  *name;
3240 
3241     PetscCall(DMGetCoordinateSection(dm, &coordSection));
3242     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
3243     PetscCall(DMGetCoordinateSection(subdm, &subCoordSection));
3244     PetscCall(PetscSectionSetNumFields(subCoordSection, 1));
3245     PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &numComp));
3246     PetscCall(PetscSectionSetFieldComponents(subCoordSection, 0, numComp));
3247     PetscCall(PetscSectionSetChart(subCoordSection, firstSubVertex, firstSubVertex + numSubVertices));
3248     for (v = 0; v < numSubVertices; ++v) {
3249       const PetscInt vertex    = subVertices[v];
3250       const PetscInt subvertex = firstSubVertex + v;
3251       PetscInt       dof;
3252 
3253       PetscCall(PetscSectionGetDof(coordSection, vertex, &dof));
3254       PetscCall(PetscSectionSetDof(subCoordSection, subvertex, dof));
3255       PetscCall(PetscSectionSetFieldDof(subCoordSection, subvertex, 0, dof));
3256     }
3257     PetscCall(PetscSectionSetUp(subCoordSection));
3258     PetscCall(PetscSectionGetStorageSize(subCoordSection, &coordSize));
3259     PetscCall(VecCreate(PETSC_COMM_SELF, &subCoordinates));
3260     PetscCall(PetscObjectGetName((PetscObject)coordinates, &name));
3261     PetscCall(PetscObjectSetName((PetscObject)subCoordinates, name));
3262     PetscCall(VecSetSizes(subCoordinates, coordSize, PETSC_DETERMINE));
3263     PetscCall(VecSetType(subCoordinates, VECSTANDARD));
3264     if (coordSize) {
3265       PetscCall(VecGetArray(coordinates, &coords));
3266       PetscCall(VecGetArray(subCoordinates, &subCoords));
3267       for (v = 0; v < numSubVertices; ++v) {
3268         const PetscInt vertex    = subVertices[v];
3269         const PetscInt subvertex = firstSubVertex + v;
3270         PetscInt       dof, off, sdof, soff, d;
3271 
3272         PetscCall(PetscSectionGetDof(coordSection, vertex, &dof));
3273         PetscCall(PetscSectionGetOffset(coordSection, vertex, &off));
3274         PetscCall(PetscSectionGetDof(subCoordSection, subvertex, &sdof));
3275         PetscCall(PetscSectionGetOffset(subCoordSection, subvertex, &soff));
3276         PetscCheck(dof == sdof, comm, PETSC_ERR_PLIB, "Coordinate dimension %" PetscInt_FMT " on subvertex %" PetscInt_FMT ", vertex %" PetscInt_FMT " should be %" PetscInt_FMT, sdof, subvertex, vertex, dof);
3277         for (d = 0; d < dof; ++d) subCoords[soff + d] = coords[off + d];
3278       }
3279       PetscCall(VecRestoreArray(coordinates, &coords));
3280       PetscCall(VecRestoreArray(subCoordinates, &subCoords));
3281     }
3282     PetscCall(DMSetCoordinatesLocal(subdm, subCoordinates));
3283     PetscCall(VecDestroy(&subCoordinates));
3284   }
3285   /* Cleanup */
3286   if (subvertexIS) PetscCall(ISRestoreIndices(subvertexIS, &subVertices));
3287   PetscCall(ISDestroy(&subvertexIS));
3288   if (subcellIS) PetscCall(ISRestoreIndices(subcellIS, &subCells));
3289   PetscCall(ISDestroy(&subcellIS));
3290   PetscFunctionReturn(PETSC_SUCCESS);
3291 }
3292 
3293 /* TODO: Fix this to properly propagate up error conditions it may find */
3294 static inline PetscInt DMPlexFilterPoint_Internal(PetscInt point, PetscInt firstSubPoint, PetscInt numSubPoints, const PetscInt subPoints[])
3295 {
3296   PetscInt       subPoint;
3297   PetscErrorCode ierr;
3298 
3299   ierr = PetscFindInt(point, numSubPoints, subPoints, &subPoint);
3300   if (ierr) return -1;
3301   return subPoint < 0 ? subPoint : firstSubPoint + subPoint;
3302 }
3303 
3304 /* TODO: Fix this to properly propagate up error conditions it may find */
3305 static inline PetscInt DMPlexFilterPointPerm_Internal(PetscInt point, PetscInt firstSubPoint, PetscInt numSubPoints, const PetscInt subPoints[], const PetscInt subIndices[])
3306 {
3307   PetscInt       subPoint;
3308   PetscErrorCode ierr;
3309 
3310   ierr = PetscFindInt(point, numSubPoints, subPoints, &subPoint);
3311   if (ierr) return -1;
3312   return subPoint < 0 ? subPoint : firstSubPoint + (subIndices ? subIndices[subPoint] : subPoint);
3313 }
3314 
3315 static PetscErrorCode DMPlexFilterLabels_Internal(DM dm, const PetscInt numSubPoints[], const PetscInt *subpoints[], const PetscInt firstSubPoint[], DM subdm)
3316 {
3317   DMLabel  depthLabel;
3318   PetscInt Nl, l, d;
3319 
3320   PetscFunctionBegin;
3321   // Reset depth label for fast lookup
3322   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
3323   PetscCall(DMLabelMakeAllInvalid_Internal(depthLabel));
3324   PetscCall(DMGetNumLabels(dm, &Nl));
3325   for (l = 0; l < Nl; ++l) {
3326     DMLabel         label, newlabel;
3327     const char     *lname;
3328     PetscBool       isDepth, isDim, isCelltype, isVTK;
3329     IS              valueIS;
3330     const PetscInt *values;
3331     PetscInt        Nv, v;
3332 
3333     PetscCall(DMGetLabelName(dm, l, &lname));
3334     PetscCall(PetscStrcmp(lname, "depth", &isDepth));
3335     PetscCall(PetscStrcmp(lname, "dim", &isDim));
3336     PetscCall(PetscStrcmp(lname, "celltype", &isCelltype));
3337     PetscCall(PetscStrcmp(lname, "vtk", &isVTK));
3338     if (isDepth || isDim || isCelltype || isVTK) continue;
3339     PetscCall(DMCreateLabel(subdm, lname));
3340     PetscCall(DMGetLabel(dm, lname, &label));
3341     PetscCall(DMGetLabel(subdm, lname, &newlabel));
3342     PetscCall(DMLabelGetDefaultValue(label, &v));
3343     PetscCall(DMLabelSetDefaultValue(newlabel, v));
3344     PetscCall(DMLabelGetValueIS(label, &valueIS));
3345     PetscCall(ISGetLocalSize(valueIS, &Nv));
3346     PetscCall(ISGetIndices(valueIS, &values));
3347     for (v = 0; v < Nv; ++v) {
3348       IS              pointIS;
3349       const PetscInt *points;
3350       PetscInt        Np, p;
3351 
3352       PetscCall(DMLabelGetStratumIS(label, values[v], &pointIS));
3353       PetscCall(ISGetLocalSize(pointIS, &Np));
3354       PetscCall(ISGetIndices(pointIS, &points));
3355       for (p = 0; p < Np; ++p) {
3356         const PetscInt point = points[p];
3357         PetscInt       subp, subdepth;
3358 
3359         PetscCall(DMPlexGetPointDepth(dm, point, &d));
3360         PetscCall(DMPlexGetDepth(subdm, &subdepth));
3361         if (d > subdepth) continue;
3362         subp = DMPlexFilterPoint_Internal(point, firstSubPoint[d], numSubPoints[d], subpoints[d]);
3363         if (subp >= 0) PetscCall(DMLabelSetValue(newlabel, subp, values[v]));
3364       }
3365       PetscCall(ISRestoreIndices(pointIS, &points));
3366       PetscCall(ISDestroy(&pointIS));
3367     }
3368     PetscCall(ISRestoreIndices(valueIS, &values));
3369     PetscCall(ISDestroy(&valueIS));
3370   }
3371   PetscFunctionReturn(PETSC_SUCCESS);
3372 }
3373 
3374 static PetscErrorCode DMPlexCreateSubmeshGeneric_Interpolated(DM dm, DMLabel label, PetscInt value, PetscBool markedFaces, PetscBool isCohesive, PetscInt cellHeight, PetscBool ignoreLabelHalo, PetscBool sanitizeSubmesh, PetscSF *ownershipTransferSF, DM subdm)
3375 {
3376   MPI_Comm         comm;
3377   DMLabel          subpointMap;
3378   IS              *subpointIS;
3379   const PetscInt **subpoints;
3380   PetscInt        *numSubPoints, *firstSubPoint, *coneNew, *orntNew;
3381   PetscInt         totSubPoints = 0, maxConeSize, dim, sdim, cdim, p, d, coordinate_type;
3382   PetscMPIInt      rank;
3383 
3384   PetscFunctionBegin;
3385   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
3386   PetscCallMPI(MPI_Comm_rank(comm, &rank));
3387   /* Create subpointMap which marks the submesh */
3388   PetscCall(DMLabelCreate(PETSC_COMM_SELF, "subpoint_map", &subpointMap));
3389   PetscCall(DMPlexSetSubpointMap(subdm, subpointMap));
3390   if (cellHeight) {
3391     if (isCohesive) PetscCall(DMPlexMarkCohesiveSubmesh_Interpolated(dm, label, value, subpointMap, subdm));
3392     else PetscCall(DMPlexMarkSubmesh_Interpolated(dm, label, value, markedFaces, PETSC_TRUE, subpointMap, subdm));
3393   } else {
3394     DMLabel         depth;
3395     IS              pointIS;
3396     const PetscInt *points;
3397     PetscInt        numPoints = 0, pStart = 0;
3398     PetscBool      *isGhost = NULL;
3399 
3400     PetscCall(DMPlexGetDepthLabel(dm, &depth));
3401     PetscCall(DMLabelGetStratumIS(label, value, &pointIS));
3402     if (pointIS) {
3403       PetscCall(ISGetIndices(pointIS, &points));
3404       PetscCall(ISGetLocalSize(pointIS, &numPoints));
3405     }
3406     if (ignoreLabelHalo) {
3407       PetscSF         pointSF;
3408       PetscInt        nleaves, pEnd;
3409       const PetscInt *ilocal = NULL;
3410 
3411       PetscCall(DMGetPointSF(dm, &pointSF));
3412       PetscCall(PetscSFGetGraph(pointSF, NULL, &nleaves, &ilocal, NULL));
3413       PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3414       PetscCall(PetscMalloc1(pEnd - pStart, &isGhost));
3415       for (p = 0; p < pEnd - pStart; ++p) isGhost[p] = PETSC_FALSE;
3416       for (p = 0; p < nleaves; ++p) isGhost[(ilocal ? ilocal[p] : p) - pStart] = PETSC_TRUE;
3417     }
3418     for (p = 0; p < numPoints; ++p) {
3419       PetscInt *closure = NULL;
3420       PetscInt  closureSize, c, pdim;
3421 
3422       if (ignoreLabelHalo && isGhost[points[p] - pStart]) continue;
3423       PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closure));
3424       for (c = 0; c < closureSize * 2; c += 2) {
3425         PetscCall(DMLabelGetValue(depth, closure[c], &pdim));
3426         PetscCall(DMLabelSetValue(subpointMap, closure[c], pdim));
3427       }
3428       PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closure));
3429     }
3430     PetscCall(PetscFree(isGhost));
3431     if (pointIS) PetscCall(ISRestoreIndices(pointIS, &points));
3432     PetscCall(ISDestroy(&pointIS));
3433   }
3434   /* Setup chart */
3435   PetscCall(DMGetDimension(dm, &dim));
3436   PetscCall(DMGetCoordinateDim(dm, &cdim));
3437   PetscCall(PetscMalloc4(dim + 1, &numSubPoints, dim + 1, &firstSubPoint, dim + 1, &subpointIS, dim + 1, &subpoints));
3438   for (d = 0; d <= dim; ++d) {
3439     PetscCall(DMLabelGetStratumSize(subpointMap, d, &numSubPoints[d]));
3440     totSubPoints += numSubPoints[d];
3441   }
3442   // Determine submesh dimension
3443   PetscCall(DMGetDimension(subdm, &sdim));
3444   if (sdim > 0) {
3445     // Calling function knows what dimension to use, and we include neighboring cells as well
3446     sdim = dim;
3447   } else {
3448     // We reset the subdimension based on what is being selected
3449     PetscInt lsdim;
3450     for (lsdim = dim; lsdim >= 0; --lsdim)
3451       if (numSubPoints[lsdim]) break;
3452     PetscCallMPI(MPIU_Allreduce(&lsdim, &sdim, 1, MPIU_INT, MPI_MAX, comm));
3453     PetscCall(DMSetDimension(subdm, sdim));
3454     PetscCall(DMSetCoordinateDim(subdm, cdim));
3455   }
3456   PetscCall(DMPlexSetChart(subdm, 0, totSubPoints));
3457   PetscCall(DMPlexSetVTKCellHeight(subdm, cellHeight));
3458   /* Set cone sizes */
3459   firstSubPoint[sdim] = 0;
3460   firstSubPoint[0]    = firstSubPoint[sdim] + numSubPoints[sdim];
3461   if (sdim > 1) firstSubPoint[sdim - 1] = firstSubPoint[0] + numSubPoints[0];
3462   if (sdim > 2) firstSubPoint[sdim - 2] = firstSubPoint[sdim - 1] + numSubPoints[sdim - 1];
3463   for (d = 0; d <= sdim; ++d) {
3464     PetscCall(DMLabelGetStratumIS(subpointMap, d, &subpointIS[d]));
3465     if (subpointIS[d]) PetscCall(ISGetIndices(subpointIS[d], &subpoints[d]));
3466   }
3467   /* We do not want this label automatically computed, instead we compute it here */
3468   PetscCall(DMCreateLabel(subdm, "celltype"));
3469   for (d = 0; d <= sdim; ++d) {
3470     for (p = 0; p < numSubPoints[d]; ++p) {
3471       const PetscInt  point    = subpoints[d][p];
3472       const PetscInt  subpoint = firstSubPoint[d] + p;
3473       const PetscInt *cone;
3474       PetscInt        coneSize;
3475 
3476       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
3477       if (cellHeight && (d == sdim)) {
3478         PetscInt coneSizeNew, c, val;
3479 
3480         PetscCall(DMPlexGetCone(dm, point, &cone));
3481         for (c = 0, coneSizeNew = 0; c < coneSize; ++c) {
3482           PetscCall(DMLabelGetValue(subpointMap, cone[c], &val));
3483           if (val >= 0) coneSizeNew++;
3484         }
3485         PetscCall(DMPlexSetConeSize(subdm, subpoint, coneSizeNew));
3486         PetscCall(DMPlexSetCellType(subdm, subpoint, DM_POLYTOPE_FV_GHOST));
3487       } else {
3488         DMPolytopeType ct;
3489 
3490         PetscCall(DMPlexSetConeSize(subdm, subpoint, coneSize));
3491         PetscCall(DMPlexGetCellType(dm, point, &ct));
3492         PetscCall(DMPlexSetCellType(subdm, subpoint, ct));
3493       }
3494     }
3495   }
3496   PetscCall(DMLabelDestroy(&subpointMap));
3497   PetscCall(DMSetUp(subdm));
3498   /* Set cones */
3499   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, NULL));
3500   PetscCall(PetscMalloc2(maxConeSize, &coneNew, maxConeSize, &orntNew));
3501   for (d = 0; d <= sdim; ++d) {
3502     for (p = 0; p < numSubPoints[d]; ++p) {
3503       const PetscInt  point    = subpoints[d][p];
3504       const PetscInt  subpoint = firstSubPoint[d] + p;
3505       const PetscInt *cone, *ornt;
3506       PetscInt        coneSize, subconeSize, coneSizeNew, c, subc, fornt = 0;
3507 
3508       if (d == sdim - 1) {
3509         const PetscInt *support, *cone, *ornt;
3510         PetscInt        supportSize, coneSize, s, subc;
3511 
3512         PetscCall(DMPlexGetSupport(dm, point, &support));
3513         PetscCall(DMPlexGetSupportSize(dm, point, &supportSize));
3514         for (s = 0; s < supportSize; ++s) {
3515           PetscBool isHybrid = PETSC_FALSE;
3516 
3517           PetscCall(DMPlexCellIsHybrid_Internal(dm, support[s], &isHybrid));
3518           if (!isHybrid) continue;
3519           PetscCall(PetscFindInt(support[s], numSubPoints[d + 1], subpoints[d + 1], &subc));
3520           if (subc >= 0) {
3521             const PetscInt ccell = subpoints[d + 1][subc];
3522 
3523             PetscCall(DMPlexGetCone(dm, ccell, &cone));
3524             PetscCall(DMPlexGetConeSize(dm, ccell, &coneSize));
3525             PetscCall(DMPlexGetConeOrientation(dm, ccell, &ornt));
3526             for (c = 0; c < coneSize; ++c) {
3527               if (cone[c] == point) {
3528                 fornt = ornt[c];
3529                 break;
3530               }
3531             }
3532             break;
3533           }
3534         }
3535       }
3536       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
3537       PetscCall(DMPlexGetConeSize(subdm, subpoint, &subconeSize));
3538       PetscCall(DMPlexGetCone(dm, point, &cone));
3539       PetscCall(DMPlexGetConeOrientation(dm, point, &ornt));
3540       for (c = 0, coneSizeNew = 0; c < coneSize; ++c) {
3541         PetscCall(PetscFindInt(cone[c], numSubPoints[d - 1], subpoints[d - 1], &subc));
3542         if (subc >= 0) {
3543           coneNew[coneSizeNew] = firstSubPoint[d - 1] + subc;
3544           orntNew[coneSizeNew] = ornt[c];
3545           ++coneSizeNew;
3546         }
3547       }
3548       PetscCheck(coneSizeNew == subconeSize, comm, PETSC_ERR_PLIB, "Number of cone points located %" PetscInt_FMT " does not match subcone size %" PetscInt_FMT, coneSizeNew, subconeSize);
3549       PetscCall(DMPlexSetCone(subdm, subpoint, coneNew));
3550       PetscCall(DMPlexSetConeOrientation(subdm, subpoint, orntNew));
3551       if (fornt < 0) PetscCall(DMPlexOrientPoint(subdm, subpoint, fornt));
3552     }
3553   }
3554   PetscCall(PetscFree2(coneNew, orntNew));
3555   PetscCall(DMPlexSymmetrize(subdm));
3556   PetscCall(DMPlexStratify(subdm));
3557   /* Build coordinates */
3558   for (coordinate_type = 0; coordinate_type < 2; ++coordinate_type) {
3559     DM           coordDM = NULL, subCoordDM = NULL;
3560     PetscSection coordSection = NULL, subCoordSection = NULL;
3561     Vec          coordinates = NULL, subCoordinates = NULL;
3562     PetscScalar *coords = NULL, *subCoords = NULL;
3563     PetscInt     bs, numComp, coordSize, firstP, lastP, firstSubP = totSubPoints, lastSubP = -1, numFields;
3564     const char  *name;
3565     PetscBool    localized = (PetscBool)coordinate_type;
3566 
3567     if (!dm->coordinates[coordinate_type].dm) continue;
3568     if (!localized) {
3569       PetscCall(DMGetCoordinateDM(dm, &coordDM));
3570       PetscCall(DMGetCoordinateDM(subdm, &subCoordDM));
3571     } else {
3572       {
3573         PetscInt  localizationHeight;
3574         PetscBool sparseLocalize;
3575 
3576         PetscCall(DMGetSparseLocalize(dm, &sparseLocalize));
3577         PetscCall(DMSetSparseLocalize(subdm, sparseLocalize));
3578         PetscCall(DMGetCoordinateDM(dm, &coordDM));
3579         PetscCall(DMGetCoordinateDM(subdm, &subCoordDM));
3580         PetscCall(DMPlexGetMaxProjectionHeight(coordDM, &localizationHeight));
3581         PetscCall(DMPlexSetMaxProjectionHeight(subCoordDM, localizationHeight - (dim - sdim) - cellHeight));
3582         PetscUseTypeMethod(subdm, createcellcoordinatedm, &subCoordDM);
3583         PetscCall(DMSetCellCoordinateDM(subdm, subCoordDM));
3584         PetscCall(DMDestroy(&subCoordDM));
3585       }
3586       PetscCall(DMGetCellCoordinateDM(dm, &coordDM));
3587       PetscCall(DMGetCellCoordinateDM(subdm, &subCoordDM));
3588     }
3589     PetscCall(DMGetNumFields(coordDM, &numFields));
3590     if (numFields > 0) {
3591       PetscFE      fe = NULL;
3592       PetscSpace   P  = NULL;
3593       PetscClassId id;
3594       PetscInt     degree;
3595 
3596       PetscCall(DMGetField(coordDM, 0, NULL, (PetscObject *)&fe));
3597       PetscCall(PetscObjectGetClassId((PetscObject)fe, &id));
3598       if (id == PETSCFE_CLASSID) {
3599         if (sdim == dim && cellHeight == 0) {
3600           /* TODO: Handle Field labels correctly */
3601           PetscCall(DMSetField(subCoordDM, 0, NULL, (PetscObject)fe));
3602           PetscCall(DMCreateDS(subCoordDM));
3603         } else {
3604           /* TODO: Reconstruct the lower-dimensional FE more robustly */
3605           PetscCall(PetscFEGetBasisSpace(fe, &P));
3606           PetscCall(PetscSpaceGetDegree(P, &degree, NULL));
3607           PetscCall(DMPlexCreateCoordinateSpace(subdm, degree, localized, PETSC_FALSE));
3608         }
3609       }
3610     }
3611     if (!localized) {
3612       PetscCall(DMGetCoordinateSection(dm, &coordSection));
3613       PetscCall(DMGetCoordinateSection(subdm, &subCoordSection));
3614       PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
3615     } else {
3616       PetscCall(DMGetCellCoordinateSection(dm, &coordSection));
3617       PetscCall(DMGetCellCoordinateSection(subdm, &subCoordSection));
3618       PetscCall(DMGetCellCoordinatesLocal(dm, &coordinates));
3619     }
3620     PetscCall(PetscSectionSetNumFields(subCoordSection, 1));
3621     PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &numComp));
3622     PetscCall(PetscSectionSetFieldComponents(subCoordSection, 0, numComp));
3623     PetscCall(PetscSectionGetChart(coordSection, &firstP, &lastP));
3624     for (d = 0; d <= sdim; ++d) {
3625       for (p = 0; p < numSubPoints[d]; ++p) {
3626         const PetscInt point    = subpoints[d][p];
3627         const PetscInt subpoint = firstSubPoint[d] + p;
3628 
3629         if (point >= firstP && point < lastP) {
3630           firstSubP = PetscMin(firstSubP, subpoint);
3631           lastSubP  = PetscMax(lastSubP, subpoint);
3632         }
3633       }
3634     }
3635     lastSubP += 1;
3636     if (firstSubP == totSubPoints) {
3637       /* Zero if there is no coordinate point. */
3638       firstSubP = 0;
3639       lastSubP  = 0;
3640     }
3641     PetscCall(PetscSectionSetChart(subCoordSection, firstSubP, lastSubP));
3642     for (d = 0; d <= sdim; ++d) {
3643       for (p = 0; p < numSubPoints[d]; ++p) {
3644         const PetscInt point    = subpoints[d][p];
3645         const PetscInt subpoint = firstSubPoint[d] + p;
3646         PetscInt       dof;
3647 
3648         if (point >= firstP && point < lastP) {
3649           PetscCall(PetscSectionGetDof(coordSection, point, &dof));
3650           PetscCall(PetscSectionSetDof(subCoordSection, subpoint, dof));
3651           PetscCall(PetscSectionSetFieldDof(subCoordSection, subpoint, 0, dof));
3652         }
3653       }
3654     }
3655     PetscCall(PetscSectionSetUp(subCoordSection));
3656     PetscCall(PetscSectionGetStorageSize(subCoordSection, &coordSize));
3657     PetscCall(VecCreate(PETSC_COMM_SELF, &subCoordinates));
3658     PetscCall(PetscObjectGetName((PetscObject)coordinates, &name));
3659     PetscCall(PetscObjectSetName((PetscObject)subCoordinates, name));
3660     PetscCall(VecSetSizes(subCoordinates, coordSize, PETSC_DETERMINE));
3661     PetscCall(VecGetBlockSize(coordinates, &bs));
3662     PetscCall(VecSetBlockSize(subCoordinates, bs));
3663     PetscCall(VecSetType(subCoordinates, VECSTANDARD));
3664     PetscCall(VecGetArray(coordinates, &coords));
3665     PetscCall(VecGetArray(subCoordinates, &subCoords));
3666     for (d = 0; d <= sdim; ++d) {
3667       for (p = 0; p < numSubPoints[d]; ++p) {
3668         const PetscInt point    = subpoints[d][p];
3669         const PetscInt subpoint = firstSubPoint[d] + p;
3670         PetscInt       dof, off, sdof, soff, d;
3671 
3672         if (point >= firstP && point < lastP) {
3673           PetscCall(PetscSectionGetDof(coordSection, point, &dof));
3674           PetscCall(PetscSectionGetOffset(coordSection, point, &off));
3675           PetscCall(PetscSectionGetDof(subCoordSection, subpoint, &sdof));
3676           PetscCall(PetscSectionGetOffset(subCoordSection, subpoint, &soff));
3677           PetscCheck(dof == sdof, comm, PETSC_ERR_PLIB, "Coordinate dimension %" PetscInt_FMT " on subpoint %" PetscInt_FMT ", point %" PetscInt_FMT " should be %" PetscInt_FMT, sdof, subpoint, point, dof);
3678           for (d = 0; d < dof; ++d) subCoords[soff + d] = coords[off + d];
3679         }
3680       }
3681     }
3682     PetscCall(VecRestoreArray(coordinates, &coords));
3683     PetscCall(VecRestoreArray(subCoordinates, &subCoords));
3684     switch (coordinate_type) {
3685     case 0:
3686       PetscCall(DMSetCoordinatesLocal(subdm, subCoordinates));
3687       break;
3688     case 1:
3689       PetscCall(DMSetCellCoordinatesLocal(subdm, subCoordinates));
3690       break;
3691     default:
3692       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "coordinate_type must be {0, 1}");
3693     }
3694     PetscCall(VecDestroy(&subCoordinates));
3695   }
3696   /* Build SF: We need this complexity because subpoints might not be selected on the owning process */
3697   {
3698     PetscSF            sfPoint, sfPointSub;
3699     IS                 subpIS;
3700     const PetscSFNode *remotePoints;
3701     PetscSFNode       *sremotePoints = NULL, *newLocalPoints = NULL, *newOwners = NULL;
3702     const PetscInt    *localPoints, *subpoints, *rootdegree;
3703     PetscInt          *slocalPoints = NULL, *sortedPoints = NULL, *sortedIndices = NULL;
3704     PetscInt           numRoots, numLeaves, numSubpoints = 0, numSubroots, numSubleaves = 0, l, sl = 0, ll = 0, pStart, pEnd, p;
3705     PetscMPIInt        rank, size;
3706 
3707     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
3708     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
3709     PetscCall(DMGetPointSF(dm, &sfPoint));
3710     PetscCall(DMGetPointSF(subdm, &sfPointSub));
3711     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3712     PetscCall(DMPlexGetChart(subdm, NULL, &numSubroots));
3713     PetscCall(DMPlexGetSubpointIS(subdm, &subpIS));
3714     if (subpIS) {
3715       PetscBool sorted = PETSC_TRUE;
3716 
3717       PetscCall(ISGetIndices(subpIS, &subpoints));
3718       PetscCall(ISGetLocalSize(subpIS, &numSubpoints));
3719       for (p = 1; p < numSubpoints; ++p) sorted = sorted && (subpoints[p] >= subpoints[p - 1]) ? PETSC_TRUE : PETSC_FALSE;
3720       if (!sorted) {
3721         PetscCall(PetscMalloc2(numSubpoints, &sortedPoints, numSubpoints, &sortedIndices));
3722         for (p = 0; p < numSubpoints; ++p) sortedIndices[p] = p;
3723         PetscCall(PetscArraycpy(sortedPoints, subpoints, numSubpoints));
3724         PetscCall(PetscSortIntWithArray(numSubpoints, sortedPoints, sortedIndices));
3725       }
3726     }
3727     PetscCall(PetscSFGetGraph(sfPoint, &numRoots, &numLeaves, &localPoints, &remotePoints));
3728     if (numRoots >= 0) {
3729       PetscCall(PetscSFComputeDegreeBegin(sfPoint, &rootdegree));
3730       PetscCall(PetscSFComputeDegreeEnd(sfPoint, &rootdegree));
3731       PetscCall(PetscMalloc2(pEnd - pStart, &newLocalPoints, numRoots, &newOwners));
3732       for (p = 0; p < pEnd - pStart; ++p) {
3733         newLocalPoints[p].rank  = -2;
3734         newLocalPoints[p].index = -2;
3735       }
3736       for (p = pStart; p < pEnd; ++p) {
3737         newOwners[p - pStart].rank  = -3;
3738         newOwners[p - pStart].index = -3;
3739       }
3740       if (sanitizeSubmesh) {
3741         /* A subpoint is forced to be owned by a rank that owns */
3742         /* a subcell that contains the subpoint in its closure. */
3743         PetscInt  cStart, cEnd, c, clSize, cl;
3744         PetscInt *ownedCells, *closure = NULL;
3745 
3746         /* claim ownership */
3747         for (p = 0; p < numSubpoints; ++p) {
3748           const PetscInt point = subpoints[p];
3749 
3750           newLocalPoints[point - pStart].rank  = rank;
3751           newLocalPoints[point - pStart].index = p;
3752         }
3753         PetscCall(DMGetDimension(subdm, &sdim));
3754         PetscCall(DMPlexGetDepthStratum(dm, sdim, &cStart, &cEnd));
3755         PetscCall(PetscMalloc1(cEnd - cStart, &ownedCells));
3756         for (c = cStart; c < cEnd; ++c) ownedCells[c - cStart] = 0;
3757         for (p = 0; p < numSubpoints; ++p) {
3758           c = subpoints[p];
3759           if (c >= cStart && c < cEnd) ownedCells[c - cStart] = 1;
3760         }
3761         for (l = 0; l < numLeaves; ++l) {
3762           c = localPoints ? localPoints[l] : l;
3763           if (c >= cStart && c < cEnd) ownedCells[c - cStart] = 0;
3764         }
3765         for (c = cStart; c < cEnd; ++c) {
3766           if (ownedCells[c - cStart] == 0) continue;
3767           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
3768           for (cl = 0; cl < clSize * 2; cl += 2) {
3769             p = closure[cl];
3770             if (newLocalPoints[p - pStart].rank < size) newLocalPoints[p - pStart].rank += size;
3771           }
3772           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
3773         }
3774         PetscCall(PetscFree(ownedCells));
3775         for (p = 0; p < numRoots; ++p) {
3776           newOwners[p].rank  = newLocalPoints[p].rank;
3777           newOwners[p].index = newLocalPoints[p].index;
3778         }
3779       } else {
3780         /* Set subleaves */
3781         for (l = 0; l < numLeaves; ++l) {
3782           const PetscInt point    = localPoints[l];
3783           const PetscInt subpoint = DMPlexFilterPointPerm_Internal(point, 0, numSubpoints, sortedPoints ? sortedPoints : subpoints, sortedIndices);
3784 
3785           if (subpoint < 0) continue;
3786           newLocalPoints[point - pStart].rank  = rank;
3787           newLocalPoints[point - pStart].index = subpoint;
3788           ++numSubleaves;
3789         }
3790         /* Must put in owned subpoints */
3791         for (p = 0; p < numSubpoints; ++p) {
3792           /* Hold on to currently owned points */
3793           if (rootdegree[subpoints[p] - pStart]) newOwners[subpoints[p] - pStart].rank = rank + size;
3794           else newOwners[subpoints[p] - pStart].rank = rank;
3795           newOwners[subpoints[p] - pStart].index = p;
3796         }
3797       }
3798       PetscCall(PetscSFReduceBegin(sfPoint, MPIU_SF_NODE, newLocalPoints, newOwners, MPI_MAXLOC));
3799       PetscCall(PetscSFReduceEnd(sfPoint, MPIU_SF_NODE, newLocalPoints, newOwners, MPI_MAXLOC));
3800       for (p = pStart; p < pEnd; ++p)
3801         if (newOwners[p - pStart].rank >= size) newOwners[p - pStart].rank -= size;
3802       if (ownershipTransferSF) {
3803         PetscSFNode *iremote1 = NULL, *newOwners1 = NULL;
3804         PetscInt    *ilocal1 = NULL;
3805         PetscInt     nleaves1, point;
3806 
3807         for (p = 0; p < numSubpoints; ++p) {
3808           point                                = subpoints[p];
3809           newLocalPoints[point - pStart].index = point;
3810         }
3811         PetscCall(PetscMalloc1(numRoots, &newOwners1));
3812         for (p = 0; p < numRoots; ++p) {
3813           newOwners1[p].rank  = -1;
3814           newOwners1[p].index = -1;
3815         }
3816         PetscCall(PetscSFReduceBegin(sfPoint, MPIU_SF_NODE, newLocalPoints, newOwners1, MPI_MAXLOC));
3817         PetscCall(PetscSFReduceEnd(sfPoint, MPIU_SF_NODE, newLocalPoints, newOwners1, MPI_MAXLOC));
3818         for (p = 0, nleaves1 = 0; p < numRoots; ++p) {
3819           if (newOwners[p].rank >= 0 && newOwners[p].rank != rank) ++nleaves1;
3820         }
3821         PetscCall(PetscMalloc1(nleaves1, &ilocal1));
3822         PetscCall(PetscMalloc1(nleaves1, &iremote1));
3823         for (p = 0, nleaves1 = 0; p < numRoots; ++p) {
3824           if (newOwners[p].rank >= 0 && newOwners[p].rank != rank) {
3825             ilocal1[nleaves1]        = pStart + p;
3826             iremote1[nleaves1].rank  = newOwners[p].rank;
3827             iremote1[nleaves1].index = newOwners1[p].index;
3828             ++nleaves1;
3829           }
3830         }
3831         PetscCall(PetscFree(newOwners1));
3832         PetscCall(PetscSFCreate(PetscObjectComm((PetscObject)sfPoint), ownershipTransferSF));
3833         PetscCall(PetscSFSetFromOptions(*ownershipTransferSF));
3834         PetscCall(PetscSFSetGraph(*ownershipTransferSF, pEnd - pStart, nleaves1, ilocal1, PETSC_OWN_POINTER, iremote1, PETSC_OWN_POINTER));
3835       }
3836       if (sanitizeSubmesh) {
3837         for (p = pStart; p < pEnd; ++p) {
3838           newLocalPoints[p - pStart].rank  = newOwners[p - pStart].rank;
3839           newLocalPoints[p - pStart].index = newOwners[p - pStart].index;
3840         }
3841       }
3842       PetscCall(PetscSFBcastBegin(sfPoint, MPIU_SF_NODE, newOwners, newLocalPoints, MPI_REPLACE));
3843       PetscCall(PetscSFBcastEnd(sfPoint, MPIU_SF_NODE, newOwners, newLocalPoints, MPI_REPLACE));
3844       if (sanitizeSubmesh) {
3845         for (p = 0; p < numSubpoints; ++p) {
3846           const PetscInt point = subpoints[p];
3847 
3848           if (newLocalPoints[point - pStart].rank >= 0 && newLocalPoints[point - pStart].rank != rank) ++sl;
3849         }
3850         PetscCall(PetscMalloc1(sl, &slocalPoints));
3851         PetscCall(PetscMalloc1(sl, &sremotePoints));
3852         for (p = 0, sl = 0; p < numSubpoints; ++p) {
3853           const PetscInt point = subpoints[p];
3854 
3855           if (newLocalPoints[point - pStart].rank >= 0 && newLocalPoints[point - pStart].rank != rank) {
3856             slocalPoints[sl]        = p;
3857             sremotePoints[sl].rank  = newLocalPoints[point - pStart].rank;
3858             sremotePoints[sl].index = newLocalPoints[point - pStart].index;
3859             PetscCheck(sremotePoints[sl].rank >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid remote rank");
3860             PetscCheck(sremotePoints[sl].index >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid remote subpoint");
3861             ++sl;
3862           }
3863         }
3864       } else {
3865         PetscCall(PetscMalloc1(numSubleaves, &slocalPoints));
3866         PetscCall(PetscMalloc1(numSubleaves, &sremotePoints));
3867         for (l = 0; l < numLeaves; ++l) {
3868           const PetscInt point    = localPoints[l];
3869           const PetscInt subpoint = DMPlexFilterPointPerm_Internal(point, 0, numSubpoints, sortedPoints ? sortedPoints : subpoints, sortedIndices);
3870 
3871           if (subpoint < 0) continue;
3872           if (newLocalPoints[point].rank == rank) {
3873             ++ll;
3874             continue;
3875           }
3876           slocalPoints[sl]        = subpoint;
3877           sremotePoints[sl].rank  = newLocalPoints[point].rank;
3878           sremotePoints[sl].index = newLocalPoints[point].index;
3879           PetscCheck(sremotePoints[sl].rank >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid remote rank for local point %" PetscInt_FMT, point);
3880           PetscCheck(sremotePoints[sl].index >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid remote subpoint for local point %" PetscInt_FMT, point);
3881           ++sl;
3882         }
3883         PetscCheck(sl + ll == numSubleaves, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Mismatch in number of subleaves %" PetscInt_FMT " + %" PetscInt_FMT " != %" PetscInt_FMT, sl, ll, numSubleaves);
3884       }
3885       PetscCall(PetscFree2(newLocalPoints, newOwners));
3886       PetscCall(PetscSFSetGraph(sfPointSub, numSubroots, sl, slocalPoints, PETSC_OWN_POINTER, sremotePoints, PETSC_OWN_POINTER));
3887     }
3888     if (subpIS) PetscCall(ISRestoreIndices(subpIS, &subpoints));
3889     PetscCall(PetscFree2(sortedPoints, sortedIndices));
3890   }
3891   /* Filter labels */
3892   PetscCall(DMPlexFilterLabels_Internal(dm, numSubPoints, subpoints, firstSubPoint, subdm));
3893   /* Cleanup */
3894   for (d = 0; d <= sdim; ++d) {
3895     if (subpointIS[d]) PetscCall(ISRestoreIndices(subpointIS[d], &subpoints[d]));
3896     PetscCall(ISDestroy(&subpointIS[d]));
3897   }
3898   PetscCall(PetscFree4(numSubPoints, firstSubPoint, subpointIS, subpoints));
3899   PetscFunctionReturn(PETSC_SUCCESS);
3900 }
3901 
3902 static PetscErrorCode DMPlexCreateSubmesh_Interpolated(DM dm, DMLabel vertexLabel, PetscInt value, PetscBool markedFaces, DM subdm)
3903 {
3904   PetscFunctionBegin;
3905   PetscCall(DMPlexCreateSubmeshGeneric_Interpolated(dm, vertexLabel, value, markedFaces, PETSC_FALSE, 1, PETSC_FALSE, PETSC_FALSE, NULL, subdm));
3906   PetscFunctionReturn(PETSC_SUCCESS);
3907 }
3908 
3909 /*@
3910   DMPlexCreateSubmesh - Extract a hypersurface from the mesh using vertices defined by a label
3911 
3912   Input Parameters:
3913 + dm          - The original mesh
3914 . vertexLabel - The `DMLabel` marking points contained in the surface
3915 . value       - The label value to use
3916 - markedFaces - `PETSC_TRUE` if surface faces are marked in addition to vertices, `PETSC_FALSE` if only vertices are marked
3917 
3918   Output Parameter:
3919 . subdm - The surface mesh
3920 
3921   Level: developer
3922 
3923   Note:
3924   This function produces a `DMLabel` mapping original points in the submesh to their depth. This can be obtained using `DMPlexGetSubpointMap()`.
3925 
3926 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSubpointMap()`, `DMGetLabel()`, `DMLabelSetValue()`
3927 @*/
3928 PetscErrorCode DMPlexCreateSubmesh(DM dm, DMLabel vertexLabel, PetscInt value, PetscBool markedFaces, DM *subdm)
3929 {
3930   DMPlexInterpolatedFlag interpolated;
3931   PetscInt               dim, cdim;
3932 
3933   PetscFunctionBegin;
3934   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3935   PetscAssertPointer(subdm, 5);
3936   PetscCall(DMGetDimension(dm, &dim));
3937   PetscCall(DMCreate(PetscObjectComm((PetscObject)dm), subdm));
3938   PetscCall(DMSetType(*subdm, DMPLEX));
3939   PetscCall(DMSetDimension(*subdm, dim - 1));
3940   PetscCall(DMGetCoordinateDim(dm, &cdim));
3941   PetscCall(DMSetCoordinateDim(*subdm, cdim));
3942   PetscCall(DMPlexIsInterpolated(dm, &interpolated));
3943   PetscCheck(interpolated != DMPLEX_INTERPOLATED_PARTIAL, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Not for partially interpolated meshes");
3944   if (interpolated) {
3945     PetscCall(DMPlexCreateSubmesh_Interpolated(dm, vertexLabel, value, markedFaces, *subdm));
3946   } else {
3947     PetscCall(DMPlexCreateSubmesh_Uninterpolated(dm, vertexLabel, value, *subdm));
3948   }
3949   PetscCall(DMPlexCopy_Internal(dm, PETSC_TRUE, PETSC_TRUE, *subdm));
3950   PetscFunctionReturn(PETSC_SUCCESS);
3951 }
3952 
3953 static PetscErrorCode DMPlexCreateCohesiveSubmesh_Uninterpolated(DM dm, PetscBool hasLagrange, const char label[], PetscInt value, DM subdm)
3954 {
3955   MPI_Comm        comm;
3956   DMLabel         subpointMap;
3957   IS              subvertexIS;
3958   const PetscInt *subVertices;
3959   PetscInt        numSubVertices, firstSubVertex, numSubCells, *subCells = NULL;
3960   PetscInt       *subface, maxConeSize, numSubFaces, firstSubFace, newFacePoint, nFV;
3961   PetscInt        c, f;
3962 
3963   PetscFunctionBegin;
3964   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
3965   /* Create subpointMap which marks the submesh */
3966   PetscCall(DMLabelCreate(PETSC_COMM_SELF, "subpoint_map", &subpointMap));
3967   PetscCall(DMPlexSetSubpointMap(subdm, subpointMap));
3968   PetscCall(DMPlexMarkCohesiveSubmesh_Uninterpolated(dm, hasLagrange, label, value, subpointMap, &numSubFaces, &nFV, &subCells, subdm));
3969   /* Setup chart */
3970   PetscCall(DMLabelGetStratumSize(subpointMap, 0, &numSubVertices));
3971   PetscCall(DMLabelGetStratumSize(subpointMap, 2, &numSubCells));
3972   PetscCall(DMPlexSetChart(subdm, 0, numSubCells + numSubFaces + numSubVertices));
3973   PetscCall(DMPlexSetVTKCellHeight(subdm, 1));
3974   /* Set cone sizes */
3975   firstSubVertex = numSubCells;
3976   firstSubFace   = numSubCells + numSubVertices;
3977   newFacePoint   = firstSubFace;
3978   PetscCall(DMLabelGetStratumIS(subpointMap, 0, &subvertexIS));
3979   if (subvertexIS) PetscCall(ISGetIndices(subvertexIS, &subVertices));
3980   for (c = 0; c < numSubCells; ++c) PetscCall(DMPlexSetConeSize(subdm, c, 1));
3981   for (f = firstSubFace; f < firstSubFace + numSubFaces; ++f) PetscCall(DMPlexSetConeSize(subdm, f, nFV));
3982   PetscCall(DMSetUp(subdm));
3983   PetscCall(DMLabelDestroy(&subpointMap));
3984   /* Create face cones */
3985   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, NULL));
3986   PetscCall(DMGetWorkArray(subdm, maxConeSize, MPIU_INT, (void **)&subface));
3987   for (c = 0; c < numSubCells; ++c) {
3988     const PetscInt  cell    = subCells[c];
3989     const PetscInt  subcell = c;
3990     const PetscInt *cone, *cells;
3991     PetscBool       isHybrid = PETSC_FALSE;
3992     PetscInt        numCells, subVertex, p, v;
3993 
3994     PetscCall(DMPlexCellIsHybrid_Internal(dm, cell, &isHybrid));
3995     if (!isHybrid) continue;
3996     PetscCall(DMPlexGetCone(dm, cell, &cone));
3997     for (v = 0; v < nFV; ++v) {
3998       PetscCall(PetscFindInt(cone[v], numSubVertices, subVertices, &subVertex));
3999       subface[v] = firstSubVertex + subVertex;
4000     }
4001     PetscCall(DMPlexSetCone(subdm, newFacePoint, subface));
4002     PetscCall(DMPlexSetCone(subdm, subcell, &newFacePoint));
4003     PetscCall(DMPlexGetJoin(dm, nFV, cone, &numCells, &cells));
4004     /* Not true in parallel
4005     PetscCheck(numCells == 2,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cohesive cells should separate two cells"); */
4006     for (p = 0; p < numCells; ++p) {
4007       PetscInt  negsubcell;
4008       PetscBool isHybrid = PETSC_FALSE;
4009 
4010       PetscCall(DMPlexCellIsHybrid_Internal(dm, cells[p], &isHybrid));
4011       if (isHybrid) continue;
4012       /* I know this is a crap search */
4013       for (negsubcell = 0; negsubcell < numSubCells; ++negsubcell) {
4014         if (subCells[negsubcell] == cells[p]) break;
4015       }
4016       PetscCheck(negsubcell != numSubCells, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Could not find negative face neighbor for cohesive cell %" PetscInt_FMT, cell);
4017       PetscCall(DMPlexSetCone(subdm, negsubcell, &newFacePoint));
4018     }
4019     PetscCall(DMPlexRestoreJoin(dm, nFV, cone, &numCells, &cells));
4020     ++newFacePoint;
4021   }
4022   PetscCall(DMRestoreWorkArray(subdm, maxConeSize, MPIU_INT, (void **)&subface));
4023   PetscCall(DMPlexSymmetrize(subdm));
4024   PetscCall(DMPlexStratify(subdm));
4025   /* Build coordinates */
4026   {
4027     PetscSection coordSection, subCoordSection;
4028     Vec          coordinates, subCoordinates;
4029     PetscScalar *coords, *subCoords;
4030     PetscInt     cdim, numComp, coordSize, v;
4031     const char  *name;
4032 
4033     PetscCall(DMGetCoordinateDim(dm, &cdim));
4034     PetscCall(DMGetCoordinateSection(dm, &coordSection));
4035     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
4036     PetscCall(DMGetCoordinateSection(subdm, &subCoordSection));
4037     PetscCall(PetscSectionSetNumFields(subCoordSection, 1));
4038     PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &numComp));
4039     PetscCall(PetscSectionSetFieldComponents(subCoordSection, 0, numComp));
4040     PetscCall(PetscSectionSetChart(subCoordSection, firstSubVertex, firstSubVertex + numSubVertices));
4041     for (v = 0; v < numSubVertices; ++v) {
4042       const PetscInt vertex    = subVertices[v];
4043       const PetscInt subvertex = firstSubVertex + v;
4044       PetscInt       dof;
4045 
4046       PetscCall(PetscSectionGetDof(coordSection, vertex, &dof));
4047       PetscCall(PetscSectionSetDof(subCoordSection, subvertex, dof));
4048       PetscCall(PetscSectionSetFieldDof(subCoordSection, subvertex, 0, dof));
4049     }
4050     PetscCall(PetscSectionSetUp(subCoordSection));
4051     PetscCall(PetscSectionGetStorageSize(subCoordSection, &coordSize));
4052     PetscCall(VecCreate(PETSC_COMM_SELF, &subCoordinates));
4053     PetscCall(PetscObjectGetName((PetscObject)coordinates, &name));
4054     PetscCall(PetscObjectSetName((PetscObject)subCoordinates, name));
4055     PetscCall(VecSetSizes(subCoordinates, coordSize, PETSC_DETERMINE));
4056     PetscCall(VecSetBlockSize(subCoordinates, cdim));
4057     PetscCall(VecSetType(subCoordinates, VECSTANDARD));
4058     PetscCall(VecGetArray(coordinates, &coords));
4059     PetscCall(VecGetArray(subCoordinates, &subCoords));
4060     for (v = 0; v < numSubVertices; ++v) {
4061       const PetscInt vertex    = subVertices[v];
4062       const PetscInt subvertex = firstSubVertex + v;
4063       PetscInt       dof, off, sdof, soff, d;
4064 
4065       PetscCall(PetscSectionGetDof(coordSection, vertex, &dof));
4066       PetscCall(PetscSectionGetOffset(coordSection, vertex, &off));
4067       PetscCall(PetscSectionGetDof(subCoordSection, subvertex, &sdof));
4068       PetscCall(PetscSectionGetOffset(subCoordSection, subvertex, &soff));
4069       PetscCheck(dof == sdof, comm, PETSC_ERR_PLIB, "Coordinate dimension %" PetscInt_FMT " on subvertex %" PetscInt_FMT ", vertex %" PetscInt_FMT " should be %" PetscInt_FMT, sdof, subvertex, vertex, dof);
4070       for (d = 0; d < dof; ++d) subCoords[soff + d] = coords[off + d];
4071     }
4072     PetscCall(VecRestoreArray(coordinates, &coords));
4073     PetscCall(VecRestoreArray(subCoordinates, &subCoords));
4074     PetscCall(DMSetCoordinatesLocal(subdm, subCoordinates));
4075     PetscCall(VecDestroy(&subCoordinates));
4076   }
4077   /* Build SF */
4078   CHKMEMQ;
4079   {
4080     PetscSF            sfPoint, sfPointSub;
4081     const PetscSFNode *remotePoints;
4082     PetscSFNode       *sremotePoints, *newLocalPoints, *newOwners;
4083     const PetscInt    *localPoints;
4084     PetscInt          *slocalPoints;
4085     PetscInt           numRoots, numLeaves, numSubRoots = numSubCells + numSubFaces + numSubVertices, numSubLeaves = 0, l, sl, ll, pStart, pEnd, p, vStart, vEnd;
4086     PetscMPIInt        rank;
4087 
4088     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
4089     PetscCall(DMGetPointSF(dm, &sfPoint));
4090     PetscCall(DMGetPointSF(subdm, &sfPointSub));
4091     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4092     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
4093     PetscCall(PetscSFGetGraph(sfPoint, &numRoots, &numLeaves, &localPoints, &remotePoints));
4094     if (numRoots >= 0) {
4095       /* Only vertices should be shared */
4096       PetscCall(PetscMalloc2(pEnd - pStart, &newLocalPoints, numRoots, &newOwners));
4097       for (p = 0; p < pEnd - pStart; ++p) {
4098         newLocalPoints[p].rank  = -2;
4099         newLocalPoints[p].index = -2;
4100       }
4101       /* Set subleaves */
4102       for (l = 0; l < numLeaves; ++l) {
4103         const PetscInt point    = localPoints[l];
4104         const PetscInt subPoint = DMPlexFilterPoint_Internal(point, firstSubVertex, numSubVertices, subVertices);
4105 
4106         PetscCheck(!(point < vStart) || !(point >= vEnd), PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Should not be mapping anything but vertices, %" PetscInt_FMT, point);
4107         if (subPoint < 0) continue;
4108         newLocalPoints[point - pStart].rank  = rank;
4109         newLocalPoints[point - pStart].index = subPoint;
4110         ++numSubLeaves;
4111       }
4112       /* Must put in owned subpoints */
4113       for (p = pStart; p < pEnd; ++p) {
4114         const PetscInt subPoint = DMPlexFilterPoint_Internal(p, firstSubVertex, numSubVertices, subVertices);
4115 
4116         if (subPoint < 0) {
4117           newOwners[p - pStart].rank  = -3;
4118           newOwners[p - pStart].index = -3;
4119         } else {
4120           newOwners[p - pStart].rank  = rank;
4121           newOwners[p - pStart].index = subPoint;
4122         }
4123       }
4124       PetscCall(PetscSFReduceBegin(sfPoint, MPIU_SF_NODE, newLocalPoints, newOwners, MPI_MAXLOC));
4125       PetscCall(PetscSFReduceEnd(sfPoint, MPIU_SF_NODE, newLocalPoints, newOwners, MPI_MAXLOC));
4126       PetscCall(PetscSFBcastBegin(sfPoint, MPIU_SF_NODE, newOwners, newLocalPoints, MPI_REPLACE));
4127       PetscCall(PetscSFBcastEnd(sfPoint, MPIU_SF_NODE, newOwners, newLocalPoints, MPI_REPLACE));
4128       PetscCall(PetscMalloc1(numSubLeaves, &slocalPoints));
4129       PetscCall(PetscMalloc1(numSubLeaves, &sremotePoints));
4130       for (l = 0, sl = 0, ll = 0; l < numLeaves; ++l) {
4131         const PetscInt point    = localPoints[l];
4132         const PetscInt subPoint = DMPlexFilterPoint_Internal(point, firstSubVertex, numSubVertices, subVertices);
4133 
4134         if (subPoint < 0) continue;
4135         if (newLocalPoints[point].rank == rank) {
4136           ++ll;
4137           continue;
4138         }
4139         slocalPoints[sl]        = subPoint;
4140         sremotePoints[sl].rank  = newLocalPoints[point].rank;
4141         sremotePoints[sl].index = newLocalPoints[point].index;
4142         PetscCheck(sremotePoints[sl].rank >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid remote rank for local point %" PetscInt_FMT, point);
4143         PetscCheck(sremotePoints[sl].index >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid remote subpoint for local point %" PetscInt_FMT, point);
4144         ++sl;
4145       }
4146       PetscCall(PetscFree2(newLocalPoints, newOwners));
4147       PetscCheck(sl + ll == numSubLeaves, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Mismatch in number of subleaves %" PetscInt_FMT " + %" PetscInt_FMT " != %" PetscInt_FMT, sl, ll, numSubLeaves);
4148       PetscCall(PetscSFSetGraph(sfPointSub, numSubRoots, sl, slocalPoints, PETSC_OWN_POINTER, sremotePoints, PETSC_OWN_POINTER));
4149     }
4150   }
4151   CHKMEMQ;
4152   /* Cleanup */
4153   if (subvertexIS) PetscCall(ISRestoreIndices(subvertexIS, &subVertices));
4154   PetscCall(ISDestroy(&subvertexIS));
4155   PetscCall(PetscFree(subCells));
4156   PetscFunctionReturn(PETSC_SUCCESS);
4157 }
4158 
4159 static PetscErrorCode DMPlexCreateCohesiveSubmesh_Interpolated(DM dm, const char labelname[], PetscInt value, DM subdm)
4160 {
4161   DMLabel label = NULL;
4162 
4163   PetscFunctionBegin;
4164   if (labelname) PetscCall(DMGetLabel(dm, labelname, &label));
4165   PetscCall(DMPlexCreateSubmeshGeneric_Interpolated(dm, label, value, PETSC_FALSE, PETSC_TRUE, 1, PETSC_FALSE, PETSC_FALSE, NULL, subdm));
4166   PetscFunctionReturn(PETSC_SUCCESS);
4167 }
4168 
4169 /*@
4170   DMPlexCreateCohesiveSubmesh - Extract from a mesh with cohesive cells the hypersurface defined by one face of the cells. Optionally, a label can be given to restrict the cells.
4171 
4172   Input Parameters:
4173 + dm          - The original mesh
4174 . hasLagrange - The mesh has Lagrange unknowns in the cohesive cells
4175 . label       - A label name, or `NULL`
4176 - value       - A label value
4177 
4178   Output Parameter:
4179 . subdm - The surface mesh
4180 
4181   Level: developer
4182 
4183   Note:
4184   This function produces a `DMLabel` mapping original points in the submesh to their depth. This can be obtained using `DMPlexGetSubpointMap()`.
4185 
4186 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSubpointMap()`, `DMPlexCreateSubmesh()`
4187 @*/
4188 PetscErrorCode DMPlexCreateCohesiveSubmesh(DM dm, PetscBool hasLagrange, const char label[], PetscInt value, DM *subdm)
4189 {
4190   PetscInt dim, cdim, depth;
4191 
4192   PetscFunctionBegin;
4193   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4194   PetscAssertPointer(subdm, 5);
4195   PetscCall(DMGetDimension(dm, &dim));
4196   PetscCall(DMPlexGetDepth(dm, &depth));
4197   PetscCall(DMCreate(PetscObjectComm((PetscObject)dm), subdm));
4198   PetscCall(DMSetType(*subdm, DMPLEX));
4199   PetscCall(DMSetDimension(*subdm, dim - 1));
4200   PetscCall(DMGetCoordinateDim(dm, &cdim));
4201   PetscCall(DMSetCoordinateDim(*subdm, cdim));
4202   if (depth == dim) {
4203     PetscCall(DMPlexCreateCohesiveSubmesh_Interpolated(dm, label, value, *subdm));
4204   } else {
4205     PetscCall(DMPlexCreateCohesiveSubmesh_Uninterpolated(dm, hasLagrange, label, value, *subdm));
4206   }
4207   PetscCall(DMPlexCopy_Internal(dm, PETSC_TRUE, PETSC_TRUE, *subdm));
4208   PetscFunctionReturn(PETSC_SUCCESS);
4209 }
4210 
4211 /*@
4212   DMPlexReorderCohesiveSupports - Ensure that face supports for cohesive end caps are ordered
4213 
4214   Not Collective
4215 
4216   Input Parameter:
4217 . dm - The `DM` containing cohesive cells
4218 
4219   Level: developer
4220 
4221   Note:
4222   For the negative size (first) face, the cohesive cell should be first in the support, and for
4223   the positive side (second) face, the cohesive cell should be second in the support.
4224 
4225 .seealso: `DMPlexConstructCohesiveCells()`, `DMPlexCreateCohesiveSubmesh()`
4226 @*/
4227 PetscErrorCode DMPlexReorderCohesiveSupports(DM dm)
4228 {
4229   PetscInt dim, cStart, cEnd;
4230 
4231   PetscFunctionBegin;
4232   PetscCall(DMGetDimension(dm, &dim));
4233   PetscCall(DMPlexGetTensorPrismBounds_Internal(dm, dim, &cStart, &cEnd));
4234   for (PetscInt c = cStart; c < cEnd; ++c) {
4235     const PetscInt *cone;
4236     PetscInt        coneSize;
4237 
4238     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
4239     PetscCall(DMPlexGetCone(dm, c, &cone));
4240     PetscCheck(coneSize >= 2, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Hybrid cell %" PetscInt_FMT " cone size %" PetscInt_FMT " must be >= 2", c, coneSize);
4241     for (PetscInt s = 0; s < 2; ++s) {
4242       const PetscInt *supp;
4243       PetscInt        suppSize, newsupp[2];
4244 
4245       PetscCall(DMPlexGetSupportSize(dm, cone[s], &suppSize));
4246       PetscCall(DMPlexGetSupport(dm, cone[s], &supp));
4247       if (suppSize == 2) {
4248         /* Reorder hybrid end cap support */
4249         if (supp[s] == c) {
4250           newsupp[0] = supp[1];
4251           newsupp[1] = supp[0];
4252         } else {
4253           newsupp[0] = supp[0];
4254           newsupp[1] = supp[1];
4255         }
4256         PetscCheck(newsupp[1 - s] == c, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Hybrid end cap %" PetscInt_FMT " support entry %" PetscInt_FMT " != %" PetscInt_FMT " hybrid cell", cone[s], newsupp[1 - s], c);
4257         PetscCall(DMPlexSetSupport(dm, cone[s], newsupp));
4258       }
4259     }
4260   }
4261   PetscFunctionReturn(PETSC_SUCCESS);
4262 }
4263 
4264 /*@
4265   DMPlexFilter - Extract a subset of mesh cells defined by a label as a separate mesh
4266 
4267   Input Parameters:
4268 + dm              - The original mesh
4269 . cellLabel       - The `DMLabel` marking cells contained in the new mesh
4270 . value           - The label value to use
4271 . ignoreLabelHalo - The flag indicating if labeled points that are in the halo are ignored
4272 - sanitizeSubmesh - The flag indicating if a subpoint is forced to be owned by a rank that owns a subcell that contains that point in its closure
4273 
4274   Output Parameters:
4275 + ownershipTransferSF - The `PetscSF` representing the ownership transfers between parent local meshes due to submeshing.
4276 - subdm               - The new mesh
4277 
4278   Level: developer
4279 
4280   Note:
4281   This function produces a `DMLabel` mapping original points in the submesh to their depth. This can be obtained using `DMPlexGetSubpointMap()`.
4282 
4283   On a given rank, the leaves of the ownershipTransferSF are the points in the local mesh that this rank gives up ownership of (in the submesh), and
4284   the remote locations for these leaves are tuples of rank and point that represent the new owners.
4285 
4286 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSubpointMap()`, `DMGetLabel()`, `DMLabelSetValue()`, `DMPlexCreateSubmesh()`
4287 @*/
4288 PetscErrorCode DMPlexFilter(DM dm, DMLabel cellLabel, PetscInt value, PetscBool ignoreLabelHalo, PetscBool sanitizeSubmesh, PetscSF *ownershipTransferSF, DM *subdm)
4289 {
4290   PetscInt dim, overlap;
4291 
4292   PetscFunctionBegin;
4293   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4294   if (ownershipTransferSF) PetscAssertPointer(ownershipTransferSF, 6);
4295   PetscAssertPointer(subdm, 7);
4296   PetscCall(DMGetDimension(dm, &dim));
4297   PetscCall(DMCreate(PetscObjectComm((PetscObject)dm), subdm));
4298   PetscCall(DMSetType(*subdm, DMPLEX));
4299   /* Extract submesh in place, could be empty on some procs, could have inconsistency if procs do not both extract a shared cell */
4300   PetscCall(DMPlexCreateSubmeshGeneric_Interpolated(dm, cellLabel, value, PETSC_FALSE, PETSC_FALSE, 0, ignoreLabelHalo, sanitizeSubmesh, ownershipTransferSF, *subdm));
4301   PetscCall(DMPlexCopy_Internal(dm, PETSC_TRUE, PETSC_TRUE, *subdm));
4302   // It is possible to obtain a surface mesh where some faces are in SF
4303   //   We should either mark the mesh as having an overlap, or delete these from the SF
4304   PetscCall(DMPlexGetOverlap(dm, &overlap));
4305   if (!overlap) {
4306     PetscSF         sf;
4307     const PetscInt *leaves;
4308     PetscInt        cStart, cEnd, Nl;
4309     PetscBool       hasSubcell = PETSC_FALSE, ghasSubcell;
4310 
4311     PetscCall(DMPlexGetHeightStratum(*subdm, 0, &cStart, &cEnd));
4312     PetscCall(DMGetPointSF(*subdm, &sf));
4313     PetscCall(PetscSFGetGraph(sf, NULL, &Nl, &leaves, NULL));
4314     for (PetscInt l = 0; l < Nl; ++l) {
4315       const PetscInt point = leaves ? leaves[l] : l;
4316 
4317       if (point >= cStart && point < cEnd) {
4318         hasSubcell = PETSC_TRUE;
4319         break;
4320       }
4321     }
4322     PetscCallMPI(MPIU_Allreduce(&hasSubcell, &ghasSubcell, 1, MPI_C_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
4323     if (ghasSubcell) PetscCall(DMPlexSetOverlap(*subdm, NULL, 1));
4324   }
4325   PetscFunctionReturn(PETSC_SUCCESS);
4326 }
4327 
4328 /*@
4329   DMPlexGetSubpointMap - Returns a `DMLabel` with point dimension as values
4330 
4331   Input Parameter:
4332 . dm - The submesh `DM`
4333 
4334   Output Parameter:
4335 . subpointMap - The `DMLabel` of all the points from the original mesh in this submesh, or `NULL` if this is not a submesh
4336 
4337   Level: developer
4338 
4339 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateSubmesh()`, `DMPlexGetSubpointIS()`
4340 @*/
4341 PetscErrorCode DMPlexGetSubpointMap(DM dm, DMLabel *subpointMap)
4342 {
4343   PetscFunctionBegin;
4344   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4345   PetscAssertPointer(subpointMap, 2);
4346   *subpointMap = ((DM_Plex *)dm->data)->subpointMap;
4347   PetscFunctionReturn(PETSC_SUCCESS);
4348 }
4349 
4350 /*@
4351   DMPlexSetSubpointMap - Sets the `DMLabel` with point dimension as values
4352 
4353   Input Parameters:
4354 + dm          - The submesh `DM`
4355 - subpointMap - The `DMLabel` of all the points from the original mesh in this submesh
4356 
4357   Level: developer
4358 
4359   Note:
4360   Should normally not be called by the user, since it is set in `DMPlexCreateSubmesh()`
4361 
4362 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateSubmesh()`, `DMPlexGetSubpointIS()`
4363 @*/
4364 PetscErrorCode DMPlexSetSubpointMap(DM dm, DMLabel subpointMap)
4365 {
4366   DM_Plex *mesh = (DM_Plex *)dm->data;
4367   DMLabel  tmp;
4368 
4369   PetscFunctionBegin;
4370   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4371   tmp               = mesh->subpointMap;
4372   mesh->subpointMap = subpointMap;
4373   PetscCall(PetscObjectReference((PetscObject)mesh->subpointMap));
4374   PetscCall(DMLabelDestroy(&tmp));
4375   PetscFunctionReturn(PETSC_SUCCESS);
4376 }
4377 
4378 static PetscErrorCode DMPlexCreateSubpointIS_Internal(DM dm, IS *subpointIS)
4379 {
4380   DMLabel  spmap;
4381   PetscInt depth, d;
4382 
4383   PetscFunctionBegin;
4384   PetscCall(DMPlexGetSubpointMap(dm, &spmap));
4385   PetscCall(DMPlexGetDepth(dm, &depth));
4386   if (spmap && depth >= 0) {
4387     DM_Plex  *mesh = (DM_Plex *)dm->data;
4388     PetscInt *points, *depths;
4389     PetscInt  pStart, pEnd, p, off;
4390 
4391     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4392     PetscCheck(!pStart, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Submeshes must start the point numbering at 0, not %" PetscInt_FMT, pStart);
4393     PetscCall(PetscMalloc1(pEnd, &points));
4394     PetscCall(DMGetWorkArray(dm, depth + 1, MPIU_INT, &depths));
4395     depths[0] = depth;
4396     depths[1] = 0;
4397     for (d = 2; d <= depth; ++d) depths[d] = depth + 1 - d;
4398     for (d = 0, off = 0; d <= depth; ++d) {
4399       const PetscInt dep = depths[d];
4400       PetscInt       depStart, depEnd, n;
4401 
4402       PetscCall(DMPlexGetDepthStratum(dm, dep, &depStart, &depEnd));
4403       PetscCall(DMLabelGetStratumSize(spmap, dep, &n));
4404       if ((d < 2 && depth > 1) || d == 1) { /* Only check vertices and cells for now since the map is broken for others */
4405         PetscCheck(n == depEnd - depStart, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "The number of mapped submesh points %" PetscInt_FMT " at depth %" PetscInt_FMT " should be %" PetscInt_FMT, n, dep, depEnd - depStart);
4406       } else {
4407         if (!n) {
4408           if (d == 0) {
4409             /* Missing cells */
4410             for (p = 0; p < depEnd - depStart; ++p, ++off) points[off] = -1;
4411           } else {
4412             /* Missing faces */
4413             for (p = 0; p < depEnd - depStart; ++p, ++off) points[off] = PETSC_INT_MAX;
4414           }
4415         }
4416       }
4417       if (n) {
4418         IS              is;
4419         const PetscInt *opoints;
4420 
4421         PetscCall(DMLabelGetStratumIS(spmap, dep, &is));
4422         PetscCall(ISGetIndices(is, &opoints));
4423         for (p = 0; p < n; ++p, ++off) points[off] = opoints[p];
4424         PetscCall(ISRestoreIndices(is, &opoints));
4425         PetscCall(ISDestroy(&is));
4426       }
4427     }
4428     PetscCall(DMRestoreWorkArray(dm, depth + 1, MPIU_INT, &depths));
4429     PetscCheck(off == pEnd, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "The number of mapped submesh points %" PetscInt_FMT " should be %" PetscInt_FMT, off, pEnd);
4430     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, pEnd, points, PETSC_OWN_POINTER, subpointIS));
4431     PetscCall(PetscObjectStateGet((PetscObject)spmap, &mesh->subpointState));
4432   }
4433   PetscFunctionReturn(PETSC_SUCCESS);
4434 }
4435 
4436 /*@
4437   DMPlexGetSubpointIS - Returns an `IS` covering the entire subdm chart with the original points as data
4438 
4439   Input Parameter:
4440 . dm - The submesh `DM`
4441 
4442   Output Parameter:
4443 . subpointIS - The `IS` of all the points from the original mesh in this submesh, or `NULL` if this is not a submesh
4444 
4445   Level: developer
4446 
4447   Note:
4448   This `IS` is guaranteed to be sorted by the construction of the submesh. However, if the filtering operation removes an entire stratum, then the strata in the submesh can be in a different order, and the `subpointIS` will only be sorted within each stratum.
4449 
4450 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateSubmesh()`, `DMPlexGetSubpointMap()`
4451 @*/
4452 PetscErrorCode DMPlexGetSubpointIS(DM dm, IS *subpointIS)
4453 {
4454   DM_Plex         *mesh = (DM_Plex *)dm->data;
4455   DMLabel          spmap;
4456   PetscObjectState state;
4457 
4458   PetscFunctionBegin;
4459   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4460   PetscAssertPointer(subpointIS, 2);
4461   PetscCall(DMPlexGetSubpointMap(dm, &spmap));
4462   PetscCall(PetscObjectStateGet((PetscObject)spmap, &state));
4463   if (state != mesh->subpointState || !mesh->subpointIS) PetscCall(DMPlexCreateSubpointIS_Internal(dm, &mesh->subpointIS));
4464   *subpointIS = mesh->subpointIS;
4465   PetscFunctionReturn(PETSC_SUCCESS);
4466 }
4467 
4468 /*@
4469   DMGetEnclosureRelation - Get the relationship between `dmA` and `dmB`
4470 
4471   Input Parameters:
4472 + dmA - The first `DM`
4473 - dmB - The second `DM`
4474 
4475   Output Parameter:
4476 . rel - The relation of `dmA` to `dmB`
4477 
4478   Level: intermediate
4479 
4480 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetEnclosurePoint()`
4481 @*/
4482 PetscErrorCode DMGetEnclosureRelation(DM dmA, DM dmB, DMEnclosureType *rel)
4483 {
4484   DM       plexA, plexB, sdm;
4485   DMLabel  spmap;
4486   PetscInt pStartA, pEndA, pStartB, pEndB, NpA, NpB;
4487 
4488   PetscFunctionBegin;
4489   PetscAssertPointer(rel, 3);
4490   *rel = DM_ENC_NONE;
4491   if (!dmA || !dmB) PetscFunctionReturn(PETSC_SUCCESS);
4492   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
4493   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
4494   if (dmA == dmB) {
4495     *rel = DM_ENC_EQUALITY;
4496     PetscFunctionReturn(PETSC_SUCCESS);
4497   }
4498   PetscCall(DMConvert(dmA, DMPLEX, &plexA));
4499   PetscCall(DMConvert(dmB, DMPLEX, &plexB));
4500   PetscCall(DMPlexGetChart(plexA, &pStartA, &pEndA));
4501   PetscCall(DMPlexGetChart(plexB, &pStartB, &pEndB));
4502   /* Assumption 1: subDMs have smaller charts than the DMs that they originate from
4503     - The degenerate case of a subdomain which includes all of the domain on some process can be treated as equality */
4504   if (pStartA == pStartB && pEndA == pEndB) {
4505     *rel = DM_ENC_EQUALITY;
4506     goto end;
4507   }
4508   NpA = pEndA - pStartA;
4509   NpB = pEndB - pStartB;
4510   if (NpA == NpB) goto end;
4511   sdm = NpA > NpB ? plexB : plexA; /* The other is the original, enclosing dm */
4512   PetscCall(DMPlexGetSubpointMap(sdm, &spmap));
4513   if (!spmap) goto end;
4514   /* TODO Check the space mapped to by subpointMap is same size as dm */
4515   if (NpA > NpB) {
4516     *rel = DM_ENC_SUPERMESH;
4517   } else {
4518     *rel = DM_ENC_SUBMESH;
4519   }
4520 end:
4521   PetscCall(DMDestroy(&plexA));
4522   PetscCall(DMDestroy(&plexB));
4523   PetscFunctionReturn(PETSC_SUCCESS);
4524 }
4525 
4526 /*@
4527   DMGetEnclosurePoint - Get the point `pA` in `dmA` which corresponds to the point `pB` in `dmB`
4528 
4529   Input Parameters:
4530 + dmA   - The first `DM`
4531 . dmB   - The second `DM`
4532 . etype - The type of enclosure relation that `dmA` has to `dmB`
4533 - pB    - A point of `dmB`
4534 
4535   Output Parameter:
4536 . pA - The corresponding point of `dmA`
4537 
4538   Level: intermediate
4539 
4540 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetEnclosureRelation()`
4541 @*/
4542 PetscErrorCode DMGetEnclosurePoint(DM dmA, DM dmB, DMEnclosureType etype, PetscInt pB, PetscInt *pA)
4543 {
4544   DM              sdm;
4545   IS              subpointIS;
4546   const PetscInt *subpoints;
4547   PetscInt        numSubpoints;
4548 
4549   PetscFunctionBegin;
4550   /* TODO Cache the IS, making it look like an index */
4551   switch (etype) {
4552   case DM_ENC_SUPERMESH:
4553     sdm = dmB;
4554     PetscCall(DMPlexGetSubpointIS(sdm, &subpointIS));
4555     PetscCall(ISGetIndices(subpointIS, &subpoints));
4556     *pA = subpoints[pB];
4557     PetscCall(ISRestoreIndices(subpointIS, &subpoints));
4558     break;
4559   case DM_ENC_SUBMESH:
4560     sdm = dmA;
4561     PetscCall(DMPlexGetSubpointIS(sdm, &subpointIS));
4562     PetscCall(ISGetLocalSize(subpointIS, &numSubpoints));
4563     PetscCall(ISGetIndices(subpointIS, &subpoints));
4564     PetscCall(PetscFindInt(pB, numSubpoints, subpoints, pA));
4565     if (*pA < 0) {
4566       PetscCall(DMViewFromOptions(dmA, NULL, "-dm_enc_A_view"));
4567       PetscCall(DMViewFromOptions(dmB, NULL, "-dm_enc_B_view"));
4568       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Point %" PetscInt_FMT " not found in submesh", pB);
4569     }
4570     PetscCall(ISRestoreIndices(subpointIS, &subpoints));
4571     break;
4572   case DM_ENC_EQUALITY:
4573   case DM_ENC_NONE:
4574     *pA = pB;
4575     break;
4576   case DM_ENC_UNKNOWN: {
4577     DMEnclosureType enc;
4578 
4579     PetscCall(DMGetEnclosureRelation(dmA, dmB, &enc));
4580     PetscCall(DMGetEnclosurePoint(dmA, dmB, enc, pB, pA));
4581   } break;
4582   default:
4583     SETERRQ(PetscObjectComm((PetscObject)dmA), PETSC_ERR_ARG_OUTOFRANGE, "Invalid enclosure type %d", (int)etype);
4584   }
4585   PetscFunctionReturn(PETSC_SUCCESS);
4586 }
4587