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