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