xref: /petsc/src/dm/impls/plex/plexcreate.c (revision 357d8704e7b71e6f14cfde34e80d63f499af80c0)
1 #define PETSCDM_DLL
2 #include <petsc/private/dmpleximpl.h> /*I   "petscdmplex.h"   I*/
3 #include <petsc/private/hashseti.h>
4 #include <petscsf.h>
5 #include <petscdmplextransform.h> /*I   "petscdmplextransform.h"   I*/
6 #include <petscdmlabelephemeral.h>
7 #include <petsc/private/kernels/blockmatmult.h>
8 #include <petsc/private/kernels/blockinvert.h>
9 
10 #ifdef PETSC_HAVE_UNISTD_H
11   #include <unistd.h>
12 #endif
13 #include <errno.h>
14 
15 PetscLogEvent DMPLEX_CreateFromFile, DMPLEX_CreateFromOptions, DMPLEX_BuildFromCellList, DMPLEX_BuildCoordinatesFromCellList;
16 
17 /* External function declarations here */
18 static PetscErrorCode DMInitialize_Plex(DM dm);
19 
20 /* This copies internal things in the Plex structure that we generally want when making a new, related Plex */
21 PetscErrorCode DMPlexCopy_Internal(DM dmin, PetscBool copyPeriodicity, PetscBool copyOverlap, DM dmout)
22 {
23   const PetscReal     *maxCell, *Lstart, *L;
24   VecType              vecType;
25   MatType              matType;
26   PetscBool            dist, useCeed;
27   DMReorderDefaultFlag reorder;
28 
29   PetscFunctionBegin;
30   PetscCall(DMGetVecType(dmin, &vecType));
31   PetscCall(DMSetVecType(dmout, vecType));
32   PetscCall(DMGetMatType(dmin, &matType));
33   PetscCall(DMSetMatType(dmout, matType));
34   if (copyPeriodicity) {
35     PetscCall(DMGetPeriodicity(dmin, &maxCell, &Lstart, &L));
36     PetscCall(DMSetPeriodicity(dmout, maxCell, Lstart, L));
37     PetscCall(DMLocalizeCoordinates(dmout));
38   }
39   PetscCall(DMPlexDistributeGetDefault(dmin, &dist));
40   PetscCall(DMPlexDistributeSetDefault(dmout, dist));
41   PetscCall(DMPlexReorderGetDefault(dmin, &reorder));
42   PetscCall(DMPlexReorderSetDefault(dmout, reorder));
43   PetscCall(DMPlexGetUseCeed(dmin, &useCeed));
44   PetscCall(DMPlexSetUseCeed(dmout, useCeed));
45   ((DM_Plex *)dmout->data)->useHashLocation = ((DM_Plex *)dmin->data)->useHashLocation;
46   ((DM_Plex *)dmout->data)->printSetValues  = ((DM_Plex *)dmin->data)->printSetValues;
47   ((DM_Plex *)dmout->data)->printFEM        = ((DM_Plex *)dmin->data)->printFEM;
48   ((DM_Plex *)dmout->data)->printFVM        = ((DM_Plex *)dmin->data)->printFVM;
49   ((DM_Plex *)dmout->data)->printL2         = ((DM_Plex *)dmin->data)->printL2;
50   ((DM_Plex *)dmout->data)->printLocate     = ((DM_Plex *)dmin->data)->printLocate;
51   ((DM_Plex *)dmout->data)->printTol        = ((DM_Plex *)dmin->data)->printTol;
52   if (copyOverlap) PetscCall(DMPlexSetOverlap_Plex(dmout, dmin, 0));
53   PetscFunctionReturn(PETSC_SUCCESS);
54 }
55 
56 /* Replace dm with the contents of ndm, and then destroy ndm
57    - Share the DM_Plex structure
58    - Share the coordinates
59    - Share the SF
60 */
61 PetscErrorCode DMPlexReplace_Internal(DM dm, DM *ndm)
62 {
63   PetscSF          sf;
64   DM               dmNew = *ndm, coordDM, coarseDM;
65   Vec              coords;
66   const PetscReal *maxCell, *Lstart, *L;
67   PetscInt         dim, cdim;
68 
69   PetscFunctionBegin;
70   if (dm == dmNew) {
71     PetscCall(DMDestroy(ndm));
72     PetscFunctionReturn(PETSC_SUCCESS);
73   }
74   dm->setupcalled = dmNew->setupcalled;
75   if (!dm->hdr.name) {
76     const char *name;
77 
78     PetscCall(PetscObjectGetName((PetscObject)*ndm, &name));
79     PetscCall(PetscObjectSetName((PetscObject)dm, name));
80   }
81   PetscCall(DMGetDimension(dmNew, &dim));
82   PetscCall(DMSetDimension(dm, dim));
83   PetscCall(DMGetCoordinateDim(dmNew, &cdim));
84   PetscCall(DMSetCoordinateDim(dm, cdim));
85   PetscCall(DMGetPointSF(dmNew, &sf));
86   PetscCall(DMSetPointSF(dm, sf));
87   PetscCall(DMGetCoordinateDM(dmNew, &coordDM));
88   PetscCall(DMGetCoordinatesLocal(dmNew, &coords));
89   PetscCall(DMSetCoordinateDM(dm, coordDM));
90   PetscCall(DMSetCoordinatesLocal(dm, coords));
91   PetscCall(DMGetCellCoordinateDM(dmNew, &coordDM));
92   PetscCall(DMGetCellCoordinatesLocal(dmNew, &coords));
93   PetscCall(DMSetCellCoordinateDM(dm, coordDM));
94   PetscCall(DMSetCellCoordinatesLocal(dm, coords));
95   /* Do not want to create the coordinate field if it does not already exist, so do not call DMGetCoordinateField() */
96   PetscCall(DMFieldDestroy(&dm->coordinates[0].field));
97   dm->coordinates[0].field            = dmNew->coordinates[0].field;
98   ((DM_Plex *)dmNew->data)->coordFunc = ((DM_Plex *)dm->data)->coordFunc;
99   PetscCall(DMGetPeriodicity(dmNew, &maxCell, &Lstart, &L));
100   PetscCall(DMSetPeriodicity(dm, maxCell, Lstart, L));
101   PetscCall(DMPlexGetGlobalToNaturalSF(dmNew, &sf));
102   PetscCall(DMPlexSetGlobalToNaturalSF(dm, sf));
103   PetscCall(DMDestroy_Plex(dm));
104   PetscCall(DMInitialize_Plex(dm));
105   dm->data = dmNew->data;
106   ((DM_Plex *)dmNew->data)->refct++;
107   {
108     PetscInt       num_face_sfs;
109     const PetscSF *sfs;
110     PetscCall(DMPlexGetIsoperiodicFaceSF(dm, &num_face_sfs, &sfs));
111     PetscCall(DMPlexSetIsoperiodicFaceSF(dm, num_face_sfs, (PetscSF *)sfs)); // for the compose function effect on dm
112   }
113   PetscCall(DMDestroyLabelLinkList_Internal(dm));
114   PetscCall(DMCopyLabels(dmNew, dm, PETSC_OWN_POINTER, PETSC_TRUE, DM_COPY_LABELS_FAIL));
115   PetscCall(DMGetCoarseDM(dmNew, &coarseDM));
116   PetscCall(DMSetCoarseDM(dm, coarseDM));
117   PetscCall(DMDestroy(ndm));
118   PetscFunctionReturn(PETSC_SUCCESS);
119 }
120 
121 /* Swap dm with the contents of dmNew
122    - Swap the DM_Plex structure
123    - Swap the coordinates
124    - Swap the point PetscSF
125 */
126 static PetscErrorCode DMPlexSwap_Static(DM dmA, DM dmB)
127 {
128   DM          coordDMA, coordDMB;
129   Vec         coordsA, coordsB;
130   PetscSF     sfA, sfB;
131   DMField     fieldTmp;
132   void       *tmp;
133   DMLabelLink listTmp;
134   DMLabel     depthTmp;
135   PetscInt    tmpI;
136 
137   PetscFunctionBegin;
138   if (dmA == dmB) PetscFunctionReturn(PETSC_SUCCESS);
139   PetscCall(DMGetPointSF(dmA, &sfA));
140   PetscCall(DMGetPointSF(dmB, &sfB));
141   PetscCall(PetscObjectReference((PetscObject)sfA));
142   PetscCall(DMSetPointSF(dmA, sfB));
143   PetscCall(DMSetPointSF(dmB, sfA));
144   PetscCall(PetscObjectDereference((PetscObject)sfA));
145 
146   PetscCall(DMGetCoordinateDM(dmA, &coordDMA));
147   PetscCall(DMGetCoordinateDM(dmB, &coordDMB));
148   PetscCall(PetscObjectReference((PetscObject)coordDMA));
149   PetscCall(DMSetCoordinateDM(dmA, coordDMB));
150   PetscCall(DMSetCoordinateDM(dmB, coordDMA));
151   PetscCall(PetscObjectDereference((PetscObject)coordDMA));
152 
153   PetscCall(DMGetCoordinatesLocal(dmA, &coordsA));
154   PetscCall(DMGetCoordinatesLocal(dmB, &coordsB));
155   PetscCall(PetscObjectReference((PetscObject)coordsA));
156   PetscCall(DMSetCoordinatesLocal(dmA, coordsB));
157   PetscCall(DMSetCoordinatesLocal(dmB, coordsA));
158   PetscCall(PetscObjectDereference((PetscObject)coordsA));
159 
160   PetscCall(DMGetCellCoordinateDM(dmA, &coordDMA));
161   PetscCall(DMGetCellCoordinateDM(dmB, &coordDMB));
162   PetscCall(PetscObjectReference((PetscObject)coordDMA));
163   PetscCall(DMSetCellCoordinateDM(dmA, coordDMB));
164   PetscCall(DMSetCellCoordinateDM(dmB, coordDMA));
165   PetscCall(PetscObjectDereference((PetscObject)coordDMA));
166 
167   PetscCall(DMGetCellCoordinatesLocal(dmA, &coordsA));
168   PetscCall(DMGetCellCoordinatesLocal(dmB, &coordsB));
169   PetscCall(PetscObjectReference((PetscObject)coordsA));
170   PetscCall(DMSetCellCoordinatesLocal(dmA, coordsB));
171   PetscCall(DMSetCellCoordinatesLocal(dmB, coordsA));
172   PetscCall(PetscObjectDereference((PetscObject)coordsA));
173 
174   fieldTmp                  = dmA->coordinates[0].field;
175   dmA->coordinates[0].field = dmB->coordinates[0].field;
176   dmB->coordinates[0].field = fieldTmp;
177   fieldTmp                  = dmA->coordinates[1].field;
178   dmA->coordinates[1].field = dmB->coordinates[1].field;
179   dmB->coordinates[1].field = fieldTmp;
180   tmp                       = dmA->data;
181   dmA->data                 = dmB->data;
182   dmB->data                 = tmp;
183   listTmp                   = dmA->labels;
184   dmA->labels               = dmB->labels;
185   dmB->labels               = listTmp;
186   depthTmp                  = dmA->depthLabel;
187   dmA->depthLabel           = dmB->depthLabel;
188   dmB->depthLabel           = depthTmp;
189   depthTmp                  = dmA->celltypeLabel;
190   dmA->celltypeLabel        = dmB->celltypeLabel;
191   dmB->celltypeLabel        = depthTmp;
192   tmpI                      = dmA->levelup;
193   dmA->levelup              = dmB->levelup;
194   dmB->levelup              = tmpI;
195   PetscFunctionReturn(PETSC_SUCCESS);
196 }
197 
198 PetscErrorCode DMPlexInterpolateInPlace_Internal(DM dm)
199 {
200   DM idm;
201 
202   PetscFunctionBegin;
203   PetscCall(DMPlexInterpolate(dm, &idm));
204   PetscCall(DMPlexCopyCoordinates(dm, idm));
205   PetscCall(DMPlexReplace_Internal(dm, &idm));
206   PetscFunctionReturn(PETSC_SUCCESS);
207 }
208 
209 /*@C
210   DMPlexCreateCoordinateSpace - Creates a finite element space for the coordinates
211 
212   Collective
213 
214   Input Parameters:
215 + dm        - The `DMPLEX`
216 . degree    - The degree of the finite element or `PETSC_DECIDE`
217 . project   - Flag to project current coordinates into the space
218 - coordFunc - An optional function to map new points from refinement to the surface
219 
220   Level: advanced
221 
222 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscPointFunc`, `PetscFECreateLagrange()`, `DMGetCoordinateDM()`
223 @*/
224 PetscErrorCode DMPlexCreateCoordinateSpace(DM dm, PetscInt degree, PetscBool project, PetscPointFunc coordFunc)
225 {
226   DM_Plex *mesh = (DM_Plex *)dm->data;
227   PetscFE  fe   = NULL;
228   DM       cdm;
229   PetscInt dim, dE, qorder, height;
230 
231   PetscFunctionBegin;
232   PetscCall(DMGetDimension(dm, &dim));
233   PetscCall(DMGetCoordinateDim(dm, &dE));
234   qorder = degree;
235   PetscCall(DMGetCoordinateDM(dm, &cdm));
236   PetscObjectOptionsBegin((PetscObject)cdm);
237   PetscCall(PetscOptionsBoundedInt("-default_quadrature_order", "Quadrature order is one less than quadrature points per edge", "DMPlexCreateCoordinateSpace", qorder, &qorder, NULL, 0));
238   PetscOptionsEnd();
239   PetscCall(DMPlexGetVTKCellHeight(dm, &height));
240   if (degree >= 0) {
241     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
242     PetscInt       cStart, cEnd, gct;
243 
244     PetscCall(DMPlexGetHeightStratum(dm, height, &cStart, &cEnd));
245     if (cEnd > cStart) PetscCall(DMPlexGetCellType(dm, cStart, &ct));
246     gct = (PetscInt)ct;
247     PetscCall(MPIU_Allreduce(MPI_IN_PLACE, &gct, 1, MPIU_INT, MPI_MIN, PetscObjectComm((PetscObject)dm)));
248     ct = (DMPolytopeType)gct;
249     // Work around current bug in PetscDualSpaceSetUp_Lagrange()
250     //   Can be seen in plex_tutorials-ex10_1
251     if (ct != DM_POLYTOPE_SEG_PRISM_TENSOR && ct != DM_POLYTOPE_TRI_PRISM_TENSOR && ct != DM_POLYTOPE_QUAD_PRISM_TENSOR) PetscCall(PetscFECreateLagrangeByCell(PETSC_COMM_SELF, dim, dE, ct, degree, qorder, &fe));
252   }
253   PetscCall(DMSetCoordinateDisc(dm, fe, project));
254   PetscCall(PetscFEDestroy(&fe));
255   mesh->coordFunc = coordFunc;
256   PetscFunctionReturn(PETSC_SUCCESS);
257 }
258 
259 /*@
260   DMPlexCreateDoublet - Creates a mesh of two cells of the specified type, optionally with later refinement.
261 
262   Collective
263 
264   Input Parameters:
265 + comm            - The communicator for the `DM` object
266 . dim             - The spatial dimension
267 . simplex         - Flag for simplicial cells, otherwise they are tensor product cells
268 . interpolate     - Flag to create intermediate mesh pieces (edges, faces)
269 - refinementLimit - A nonzero number indicates the largest admissible volume for a refined cell
270 
271   Output Parameter:
272 . newdm - The `DM` object
273 
274   Level: beginner
275 
276 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetType()`, `DMCreate()`
277 @*/
278 PetscErrorCode DMPlexCreateDoublet(MPI_Comm comm, PetscInt dim, PetscBool simplex, PetscBool interpolate, PetscReal refinementLimit, DM *newdm)
279 {
280   DM          dm;
281   PetscMPIInt rank;
282 
283   PetscFunctionBegin;
284   PetscCall(DMCreate(comm, &dm));
285   PetscCall(DMSetType(dm, DMPLEX));
286   PetscCall(DMSetDimension(dm, dim));
287   PetscCall(PetscLogEventBegin(DMPLEX_Generate, dm, 0, 0, 0));
288   PetscCallMPI(MPI_Comm_rank(comm, &rank));
289   switch (dim) {
290   case 2:
291     if (simplex) PetscCall(PetscObjectSetName((PetscObject)dm, "triangular"));
292     else PetscCall(PetscObjectSetName((PetscObject)dm, "quadrilateral"));
293     break;
294   case 3:
295     if (simplex) PetscCall(PetscObjectSetName((PetscObject)dm, "tetrahedral"));
296     else PetscCall(PetscObjectSetName((PetscObject)dm, "hexahedral"));
297     break;
298   default:
299     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Cannot make meshes for dimension %" PetscInt_FMT, dim);
300   }
301   if (rank) {
302     PetscInt numPoints[2] = {0, 0};
303     PetscCall(DMPlexCreateFromDAG(dm, 1, numPoints, NULL, NULL, NULL, NULL));
304   } else {
305     switch (dim) {
306     case 2:
307       if (simplex) {
308         PetscInt    numPoints[2]        = {4, 2};
309         PetscInt    coneSize[6]         = {3, 3, 0, 0, 0, 0};
310         PetscInt    cones[6]            = {2, 3, 4, 5, 4, 3};
311         PetscInt    coneOrientations[6] = {0, 0, 0, 0, 0, 0};
312         PetscScalar vertexCoords[8]     = {-0.5, 0.5, 0.0, 0.0, 0.0, 1.0, 0.5, 0.5};
313 
314         PetscCall(DMPlexCreateFromDAG(dm, 1, numPoints, coneSize, cones, coneOrientations, vertexCoords));
315       } else {
316         PetscInt    numPoints[2]        = {6, 2};
317         PetscInt    coneSize[8]         = {4, 4, 0, 0, 0, 0, 0, 0};
318         PetscInt    cones[8]            = {2, 3, 4, 5, 3, 6, 7, 4};
319         PetscInt    coneOrientations[8] = {0, 0, 0, 0, 0, 0, 0, 0};
320         PetscScalar vertexCoords[12]    = {-1.0, -0.5, 0.0, -0.5, 0.0, 0.5, -1.0, 0.5, 1.0, -0.5, 1.0, 0.5};
321 
322         PetscCall(DMPlexCreateFromDAG(dm, 1, numPoints, coneSize, cones, coneOrientations, vertexCoords));
323       }
324       break;
325     case 3:
326       if (simplex) {
327         PetscInt    numPoints[2]        = {5, 2};
328         PetscInt    coneSize[7]         = {4, 4, 0, 0, 0, 0, 0};
329         PetscInt    cones[8]            = {4, 3, 5, 2, 5, 3, 4, 6};
330         PetscInt    coneOrientations[8] = {0, 0, 0, 0, 0, 0, 0, 0};
331         PetscScalar vertexCoords[15]    = {-1.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0};
332 
333         PetscCall(DMPlexCreateFromDAG(dm, 1, numPoints, coneSize, cones, coneOrientations, vertexCoords));
334       } else {
335         PetscInt    numPoints[2]         = {12, 2};
336         PetscInt    coneSize[14]         = {8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
337         PetscInt    cones[16]            = {2, 3, 4, 5, 6, 7, 8, 9, 5, 4, 10, 11, 7, 12, 13, 8};
338         PetscInt    coneOrientations[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
339         PetscScalar vertexCoords[36]     = {-1.0, -0.5, -0.5, -1.0, 0.5, -0.5, 0.0, 0.5, -0.5, 0.0, -0.5, -0.5, -1.0, -0.5, 0.5, 0.0, -0.5, 0.5, 0.0, 0.5, 0.5, -1.0, 0.5, 0.5, 1.0, 0.5, -0.5, 1.0, -0.5, -0.5, 1.0, -0.5, 0.5, 1.0, 0.5, 0.5};
340 
341         PetscCall(DMPlexCreateFromDAG(dm, 1, numPoints, coneSize, cones, coneOrientations, vertexCoords));
342       }
343       break;
344     default:
345       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Cannot make meshes for dimension %" PetscInt_FMT, dim);
346     }
347   }
348   PetscCall(PetscLogEventEnd(DMPLEX_Generate, dm, 0, 0, 0));
349   *newdm = dm;
350   if (refinementLimit > 0.0) {
351     DM          rdm;
352     const char *name;
353 
354     PetscCall(DMPlexSetRefinementUniform(*newdm, PETSC_FALSE));
355     PetscCall(DMPlexSetRefinementLimit(*newdm, refinementLimit));
356     PetscCall(DMRefine(*newdm, comm, &rdm));
357     PetscCall(PetscObjectGetName((PetscObject)*newdm, &name));
358     PetscCall(PetscObjectSetName((PetscObject)rdm, name));
359     PetscCall(DMDestroy(newdm));
360     *newdm = rdm;
361   }
362   if (interpolate) {
363     DM idm;
364 
365     PetscCall(DMPlexInterpolate(*newdm, &idm));
366     PetscCall(DMDestroy(newdm));
367     *newdm = idm;
368   }
369   PetscFunctionReturn(PETSC_SUCCESS);
370 }
371 
372 static PetscErrorCode DMPlexCreateBoxSurfaceMesh_Tensor_1D_Internal(DM dm, const PetscReal lower[], const PetscReal upper[], const PetscInt edges[])
373 {
374   const PetscInt numVertices    = 2;
375   PetscInt       markerRight    = 1;
376   PetscInt       markerLeft     = 1;
377   PetscBool      markerSeparate = PETSC_FALSE;
378   Vec            coordinates;
379   PetscSection   coordSection;
380   PetscScalar   *coords;
381   PetscInt       coordSize;
382   PetscMPIInt    rank;
383   PetscInt       cdim = 1, v;
384 
385   PetscFunctionBegin;
386   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_separate_marker", &markerSeparate, NULL));
387   if (markerSeparate) {
388     markerRight = 2;
389     markerLeft  = 1;
390   }
391   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
392   if (rank == 0) {
393     PetscCall(DMPlexSetChart(dm, 0, numVertices));
394     PetscCall(DMSetUp(dm)); /* Allocate space for cones */
395     PetscCall(DMSetLabelValue(dm, "marker", 0, markerLeft));
396     PetscCall(DMSetLabelValue(dm, "marker", 1, markerRight));
397   }
398   PetscCall(DMPlexSymmetrize(dm));
399   PetscCall(DMPlexStratify(dm));
400   /* Build coordinates */
401   PetscCall(DMSetCoordinateDim(dm, cdim));
402   PetscCall(DMGetCoordinateSection(dm, &coordSection));
403   PetscCall(PetscSectionSetNumFields(coordSection, 1));
404   PetscCall(PetscSectionSetChart(coordSection, 0, numVertices));
405   PetscCall(PetscSectionSetFieldComponents(coordSection, 0, cdim));
406   for (v = 0; v < numVertices; ++v) {
407     PetscCall(PetscSectionSetDof(coordSection, v, cdim));
408     PetscCall(PetscSectionSetFieldDof(coordSection, v, 0, cdim));
409   }
410   PetscCall(PetscSectionSetUp(coordSection));
411   PetscCall(PetscSectionGetStorageSize(coordSection, &coordSize));
412   PetscCall(VecCreate(PETSC_COMM_SELF, &coordinates));
413   PetscCall(PetscObjectSetName((PetscObject)coordinates, "coordinates"));
414   PetscCall(VecSetSizes(coordinates, coordSize, PETSC_DETERMINE));
415   PetscCall(VecSetBlockSize(coordinates, cdim));
416   PetscCall(VecSetType(coordinates, VECSTANDARD));
417   PetscCall(VecGetArray(coordinates, &coords));
418   coords[0] = lower[0];
419   coords[1] = upper[0];
420   PetscCall(VecRestoreArray(coordinates, &coords));
421   PetscCall(DMSetCoordinatesLocal(dm, coordinates));
422   PetscCall(VecDestroy(&coordinates));
423   PetscFunctionReturn(PETSC_SUCCESS);
424 }
425 
426 static PetscErrorCode DMPlexCreateBoxSurfaceMesh_Tensor_2D_Internal(DM dm, const PetscReal lower[], const PetscReal upper[], const PetscInt edges[])
427 {
428   const PetscInt numVertices    = (edges[0] + 1) * (edges[1] + 1);
429   const PetscInt numEdges       = edges[0] * (edges[1] + 1) + (edges[0] + 1) * edges[1];
430   PetscInt       markerTop      = 1;
431   PetscInt       markerBottom   = 1;
432   PetscInt       markerRight    = 1;
433   PetscInt       markerLeft     = 1;
434   PetscBool      markerSeparate = PETSC_FALSE;
435   Vec            coordinates;
436   PetscSection   coordSection;
437   PetscScalar   *coords;
438   PetscInt       coordSize;
439   PetscMPIInt    rank;
440   PetscInt       v, vx, vy;
441 
442   PetscFunctionBegin;
443   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_separate_marker", &markerSeparate, NULL));
444   if (markerSeparate) {
445     markerTop    = 3;
446     markerBottom = 1;
447     markerRight  = 2;
448     markerLeft   = 4;
449   }
450   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
451   if (rank == 0) {
452     PetscInt e, ex, ey;
453 
454     PetscCall(DMPlexSetChart(dm, 0, numEdges + numVertices));
455     for (e = 0; e < numEdges; ++e) PetscCall(DMPlexSetConeSize(dm, e, 2));
456     PetscCall(DMSetUp(dm)); /* Allocate space for cones */
457     for (vx = 0; vx <= edges[0]; vx++) {
458       for (ey = 0; ey < edges[1]; ey++) {
459         PetscInt edge   = vx * edges[1] + ey + edges[0] * (edges[1] + 1);
460         PetscInt vertex = ey * (edges[0] + 1) + vx + numEdges;
461         PetscInt cone[2];
462 
463         cone[0] = vertex;
464         cone[1] = vertex + edges[0] + 1;
465         PetscCall(DMPlexSetCone(dm, edge, cone));
466         if (vx == edges[0]) {
467           PetscCall(DMSetLabelValue(dm, "marker", edge, markerRight));
468           PetscCall(DMSetLabelValue(dm, "marker", cone[0], markerRight));
469           if (ey == edges[1] - 1) {
470             PetscCall(DMSetLabelValue(dm, "marker", cone[1], markerRight));
471             PetscCall(DMSetLabelValue(dm, "Face Sets", cone[1], markerRight));
472           }
473         } else if (vx == 0) {
474           PetscCall(DMSetLabelValue(dm, "marker", edge, markerLeft));
475           PetscCall(DMSetLabelValue(dm, "marker", cone[0], markerLeft));
476           if (ey == edges[1] - 1) {
477             PetscCall(DMSetLabelValue(dm, "marker", cone[1], markerLeft));
478             PetscCall(DMSetLabelValue(dm, "Face Sets", cone[1], markerLeft));
479           }
480         }
481       }
482     }
483     for (vy = 0; vy <= edges[1]; vy++) {
484       for (ex = 0; ex < edges[0]; ex++) {
485         PetscInt edge   = vy * edges[0] + ex;
486         PetscInt vertex = vy * (edges[0] + 1) + ex + numEdges;
487         PetscInt cone[2];
488 
489         cone[0] = vertex;
490         cone[1] = vertex + 1;
491         PetscCall(DMPlexSetCone(dm, edge, cone));
492         if (vy == edges[1]) {
493           PetscCall(DMSetLabelValue(dm, "marker", edge, markerTop));
494           PetscCall(DMSetLabelValue(dm, "marker", cone[0], markerTop));
495           if (ex == edges[0] - 1) {
496             PetscCall(DMSetLabelValue(dm, "marker", cone[1], markerTop));
497             PetscCall(DMSetLabelValue(dm, "Face Sets", cone[1], markerTop));
498           }
499         } else if (vy == 0) {
500           PetscCall(DMSetLabelValue(dm, "marker", edge, markerBottom));
501           PetscCall(DMSetLabelValue(dm, "marker", cone[0], markerBottom));
502           if (ex == edges[0] - 1) {
503             PetscCall(DMSetLabelValue(dm, "marker", cone[1], markerBottom));
504             PetscCall(DMSetLabelValue(dm, "Face Sets", cone[1], markerBottom));
505           }
506         }
507       }
508     }
509   }
510   PetscCall(DMPlexSymmetrize(dm));
511   PetscCall(DMPlexStratify(dm));
512   /* Build coordinates */
513   PetscCall(DMSetCoordinateDim(dm, 2));
514   PetscCall(DMGetCoordinateSection(dm, &coordSection));
515   PetscCall(PetscSectionSetNumFields(coordSection, 1));
516   PetscCall(PetscSectionSetChart(coordSection, numEdges, numEdges + numVertices));
517   PetscCall(PetscSectionSetFieldComponents(coordSection, 0, 2));
518   for (v = numEdges; v < numEdges + numVertices; ++v) {
519     PetscCall(PetscSectionSetDof(coordSection, v, 2));
520     PetscCall(PetscSectionSetFieldDof(coordSection, v, 0, 2));
521   }
522   PetscCall(PetscSectionSetUp(coordSection));
523   PetscCall(PetscSectionGetStorageSize(coordSection, &coordSize));
524   PetscCall(VecCreate(PETSC_COMM_SELF, &coordinates));
525   PetscCall(PetscObjectSetName((PetscObject)coordinates, "coordinates"));
526   PetscCall(VecSetSizes(coordinates, coordSize, PETSC_DETERMINE));
527   PetscCall(VecSetBlockSize(coordinates, 2));
528   PetscCall(VecSetType(coordinates, VECSTANDARD));
529   PetscCall(VecGetArray(coordinates, &coords));
530   for (vy = 0; vy <= edges[1]; ++vy) {
531     for (vx = 0; vx <= edges[0]; ++vx) {
532       coords[(vy * (edges[0] + 1) + vx) * 2 + 0] = lower[0] + ((upper[0] - lower[0]) / edges[0]) * vx;
533       coords[(vy * (edges[0] + 1) + vx) * 2 + 1] = lower[1] + ((upper[1] - lower[1]) / edges[1]) * vy;
534     }
535   }
536   PetscCall(VecRestoreArray(coordinates, &coords));
537   PetscCall(DMSetCoordinatesLocal(dm, coordinates));
538   PetscCall(VecDestroy(&coordinates));
539   PetscFunctionReturn(PETSC_SUCCESS);
540 }
541 
542 static PetscErrorCode DMPlexCreateBoxSurfaceMesh_Tensor_3D_Internal(DM dm, const PetscReal lower[], const PetscReal upper[], const PetscInt faces[])
543 {
544   PetscInt     vertices[3], numVertices;
545   PetscInt     numFaces       = 2 * faces[0] * faces[1] + 2 * faces[1] * faces[2] + 2 * faces[0] * faces[2];
546   PetscInt     markerTop      = 1;
547   PetscInt     markerBottom   = 1;
548   PetscInt     markerFront    = 1;
549   PetscInt     markerBack     = 1;
550   PetscInt     markerRight    = 1;
551   PetscInt     markerLeft     = 1;
552   PetscBool    markerSeparate = PETSC_FALSE;
553   Vec          coordinates;
554   PetscSection coordSection;
555   PetscScalar *coords;
556   PetscInt     coordSize;
557   PetscMPIInt  rank;
558   PetscInt     v, vx, vy, vz;
559   PetscInt     voffset, iface = 0, cone[4];
560 
561   PetscFunctionBegin;
562   PetscCheck(faces[0] >= 1 && faces[1] >= 1 && faces[2] >= 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Must have at least 1 face per side");
563   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
564   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_separate_marker", &markerSeparate, NULL));
565   if (markerSeparate) {
566     markerBottom = 1;
567     markerTop    = 2;
568     markerFront  = 3;
569     markerBack   = 4;
570     markerRight  = 5;
571     markerLeft   = 6;
572   }
573   vertices[0] = faces[0] + 1;
574   vertices[1] = faces[1] + 1;
575   vertices[2] = faces[2] + 1;
576   numVertices = vertices[0] * vertices[1] * vertices[2];
577   if (rank == 0) {
578     PetscInt f;
579 
580     PetscCall(DMPlexSetChart(dm, 0, numFaces + numVertices));
581     for (f = 0; f < numFaces; ++f) PetscCall(DMPlexSetConeSize(dm, f, 4));
582     PetscCall(DMSetUp(dm)); /* Allocate space for cones */
583 
584     /* Side 0 (Top) */
585     for (vy = 0; vy < faces[1]; vy++) {
586       for (vx = 0; vx < faces[0]; vx++) {
587         voffset = numFaces + vertices[0] * vertices[1] * (vertices[2] - 1) + vy * vertices[0] + vx;
588         cone[0] = voffset;
589         cone[1] = voffset + 1;
590         cone[2] = voffset + vertices[0] + 1;
591         cone[3] = voffset + vertices[0];
592         PetscCall(DMPlexSetCone(dm, iface, cone));
593         PetscCall(DMSetLabelValue(dm, "marker", iface, markerTop));
594         PetscCall(DMSetLabelValue(dm, "marker", voffset + 0, markerTop));
595         PetscCall(DMSetLabelValue(dm, "marker", voffset + 1, markerTop));
596         PetscCall(DMSetLabelValue(dm, "marker", voffset + vertices[0] + 0, markerTop));
597         PetscCall(DMSetLabelValue(dm, "marker", voffset + vertices[0] + 1, markerTop));
598         iface++;
599       }
600     }
601 
602     /* Side 1 (Bottom) */
603     for (vy = 0; vy < faces[1]; vy++) {
604       for (vx = 0; vx < faces[0]; vx++) {
605         voffset = numFaces + vy * (faces[0] + 1) + vx;
606         cone[0] = voffset + 1;
607         cone[1] = voffset;
608         cone[2] = voffset + vertices[0];
609         cone[3] = voffset + vertices[0] + 1;
610         PetscCall(DMPlexSetCone(dm, iface, cone));
611         PetscCall(DMSetLabelValue(dm, "marker", iface, markerBottom));
612         PetscCall(DMSetLabelValue(dm, "marker", voffset + 0, markerBottom));
613         PetscCall(DMSetLabelValue(dm, "marker", voffset + 1, markerBottom));
614         PetscCall(DMSetLabelValue(dm, "marker", voffset + vertices[0] + 0, markerBottom));
615         PetscCall(DMSetLabelValue(dm, "marker", voffset + vertices[0] + 1, markerBottom));
616         iface++;
617       }
618     }
619 
620     /* Side 2 (Front) */
621     for (vz = 0; vz < faces[2]; vz++) {
622       for (vx = 0; vx < faces[0]; vx++) {
623         voffset = numFaces + vz * vertices[0] * vertices[1] + vx;
624         cone[0] = voffset;
625         cone[1] = voffset + 1;
626         cone[2] = voffset + vertices[0] * vertices[1] + 1;
627         cone[3] = voffset + vertices[0] * vertices[1];
628         PetscCall(DMPlexSetCone(dm, iface, cone));
629         PetscCall(DMSetLabelValue(dm, "marker", iface, markerFront));
630         PetscCall(DMSetLabelValue(dm, "marker", voffset + 0, markerFront));
631         PetscCall(DMSetLabelValue(dm, "marker", voffset + 1, markerFront));
632         PetscCall(DMSetLabelValue(dm, "marker", voffset + vertices[0] * vertices[1] + 0, markerFront));
633         PetscCall(DMSetLabelValue(dm, "marker", voffset + vertices[0] * vertices[1] + 1, markerFront));
634         iface++;
635       }
636     }
637 
638     /* Side 3 (Back) */
639     for (vz = 0; vz < faces[2]; vz++) {
640       for (vx = 0; vx < faces[0]; vx++) {
641         voffset = numFaces + vz * vertices[0] * vertices[1] + vertices[0] * (vertices[1] - 1) + vx;
642         cone[0] = voffset + vertices[0] * vertices[1];
643         cone[1] = voffset + vertices[0] * vertices[1] + 1;
644         cone[2] = voffset + 1;
645         cone[3] = voffset;
646         PetscCall(DMPlexSetCone(dm, iface, cone));
647         PetscCall(DMSetLabelValue(dm, "marker", iface, markerBack));
648         PetscCall(DMSetLabelValue(dm, "marker", voffset + 0, markerBack));
649         PetscCall(DMSetLabelValue(dm, "marker", voffset + 1, markerBack));
650         PetscCall(DMSetLabelValue(dm, "marker", voffset + vertices[0] * vertices[1] + 0, markerBack));
651         PetscCall(DMSetLabelValue(dm, "marker", voffset + vertices[0] * vertices[1] + 1, markerBack));
652         iface++;
653       }
654     }
655 
656     /* Side 4 (Left) */
657     for (vz = 0; vz < faces[2]; vz++) {
658       for (vy = 0; vy < faces[1]; vy++) {
659         voffset = numFaces + vz * vertices[0] * vertices[1] + vy * vertices[0];
660         cone[0] = voffset;
661         cone[1] = voffset + vertices[0] * vertices[1];
662         cone[2] = voffset + vertices[0] * vertices[1] + vertices[0];
663         cone[3] = voffset + vertices[0];
664         PetscCall(DMPlexSetCone(dm, iface, cone));
665         PetscCall(DMSetLabelValue(dm, "marker", iface, markerLeft));
666         PetscCall(DMSetLabelValue(dm, "marker", voffset + 0, markerLeft));
667         PetscCall(DMSetLabelValue(dm, "marker", voffset + vertices[0] + 0, markerLeft));
668         PetscCall(DMSetLabelValue(dm, "marker", voffset + vertices[1] + 0, markerLeft));
669         PetscCall(DMSetLabelValue(dm, "marker", voffset + vertices[0] * vertices[1] + vertices[0], markerLeft));
670         iface++;
671       }
672     }
673 
674     /* Side 5 (Right) */
675     for (vz = 0; vz < faces[2]; vz++) {
676       for (vy = 0; vy < faces[1]; vy++) {
677         voffset = numFaces + vz * vertices[0] * vertices[1] + vy * vertices[0] + faces[0];
678         cone[0] = voffset + vertices[0] * vertices[1];
679         cone[1] = voffset;
680         cone[2] = voffset + vertices[0];
681         cone[3] = voffset + vertices[0] * vertices[1] + vertices[0];
682         PetscCall(DMPlexSetCone(dm, iface, cone));
683         PetscCall(DMSetLabelValue(dm, "marker", iface, markerRight));
684         PetscCall(DMSetLabelValue(dm, "marker", voffset + 0, markerRight));
685         PetscCall(DMSetLabelValue(dm, "marker", voffset + vertices[0] + 0, markerRight));
686         PetscCall(DMSetLabelValue(dm, "marker", voffset + vertices[0] * vertices[1] + 0, markerRight));
687         PetscCall(DMSetLabelValue(dm, "marker", voffset + vertices[0] * vertices[1] + vertices[0], markerRight));
688         iface++;
689       }
690     }
691   }
692   PetscCall(DMPlexSymmetrize(dm));
693   PetscCall(DMPlexStratify(dm));
694   /* Build coordinates */
695   PetscCall(DMSetCoordinateDim(dm, 3));
696   PetscCall(DMGetCoordinateSection(dm, &coordSection));
697   PetscCall(PetscSectionSetNumFields(coordSection, 1));
698   PetscCall(PetscSectionSetChart(coordSection, numFaces, numFaces + numVertices));
699   PetscCall(PetscSectionSetFieldComponents(coordSection, 0, 3));
700   for (v = numFaces; v < numFaces + numVertices; ++v) {
701     PetscCall(PetscSectionSetDof(coordSection, v, 3));
702     PetscCall(PetscSectionSetFieldDof(coordSection, v, 0, 3));
703   }
704   PetscCall(PetscSectionSetUp(coordSection));
705   PetscCall(PetscSectionGetStorageSize(coordSection, &coordSize));
706   PetscCall(VecCreate(PETSC_COMM_SELF, &coordinates));
707   PetscCall(PetscObjectSetName((PetscObject)coordinates, "coordinates"));
708   PetscCall(VecSetSizes(coordinates, coordSize, PETSC_DETERMINE));
709   PetscCall(VecSetBlockSize(coordinates, 3));
710   PetscCall(VecSetType(coordinates, VECSTANDARD));
711   PetscCall(VecGetArray(coordinates, &coords));
712   for (vz = 0; vz <= faces[2]; ++vz) {
713     for (vy = 0; vy <= faces[1]; ++vy) {
714       for (vx = 0; vx <= faces[0]; ++vx) {
715         coords[((vz * (faces[1] + 1) + vy) * (faces[0] + 1) + vx) * 3 + 0] = lower[0] + ((upper[0] - lower[0]) / faces[0]) * vx;
716         coords[((vz * (faces[1] + 1) + vy) * (faces[0] + 1) + vx) * 3 + 1] = lower[1] + ((upper[1] - lower[1]) / faces[1]) * vy;
717         coords[((vz * (faces[1] + 1) + vy) * (faces[0] + 1) + vx) * 3 + 2] = lower[2] + ((upper[2] - lower[2]) / faces[2]) * vz;
718       }
719     }
720   }
721   PetscCall(VecRestoreArray(coordinates, &coords));
722   PetscCall(DMSetCoordinatesLocal(dm, coordinates));
723   PetscCall(VecDestroy(&coordinates));
724   PetscFunctionReturn(PETSC_SUCCESS);
725 }
726 
727 static PetscErrorCode DMPlexCreateBoxSurfaceMesh_Internal(DM dm, PetscInt dim, const PetscInt faces[], const PetscReal lower[], const PetscReal upper[], PetscBool interpolate)
728 {
729   PetscFunctionBegin;
730   PetscValidLogicalCollectiveInt(dm, dim, 2);
731   PetscCall(PetscLogEventBegin(DMPLEX_Generate, dm, 0, 0, 0));
732   PetscCall(DMSetDimension(dm, dim - 1));
733   PetscCall(DMSetCoordinateDim(dm, dim));
734   switch (dim) {
735   case 1:
736     PetscCall(DMPlexCreateBoxSurfaceMesh_Tensor_1D_Internal(dm, lower, upper, faces));
737     break;
738   case 2:
739     PetscCall(DMPlexCreateBoxSurfaceMesh_Tensor_2D_Internal(dm, lower, upper, faces));
740     break;
741   case 3:
742     PetscCall(DMPlexCreateBoxSurfaceMesh_Tensor_3D_Internal(dm, lower, upper, faces));
743     break;
744   default:
745     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Dimension not supported: %" PetscInt_FMT, dim);
746   }
747   PetscCall(PetscLogEventEnd(DMPLEX_Generate, dm, 0, 0, 0));
748   if (interpolate) PetscCall(DMPlexInterpolateInPlace_Internal(dm));
749   PetscFunctionReturn(PETSC_SUCCESS);
750 }
751 
752 /*@C
753   DMPlexCreateBoxSurfaceMesh - Creates a mesh on the surface of the tensor product of unit intervals (box) using tensor cells (hexahedra).
754 
755   Collective
756 
757   Input Parameters:
758 + comm        - The communicator for the `DM` object
759 . dim         - The spatial dimension of the box, so the resulting mesh is has dimension `dim`-1
760 . faces       - Number of faces per dimension, or `NULL` for (1,) in 1D and (2, 2) in 2D and (1, 1, 1) in 3D
761 . lower       - The lower left corner, or `NULL` for (0, 0, 0)
762 . upper       - The upper right corner, or `NULL` for (1, 1, 1)
763 - interpolate - Flag to create intermediate mesh pieces (edges, faces)
764 
765   Output Parameter:
766 . dm - The `DM` object
767 
768   Level: beginner
769 
770 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreateBoxMesh()`, `DMPlexCreateFromFile()`, `DMSetType()`, `DMCreate()`
771 @*/
772 PetscErrorCode DMPlexCreateBoxSurfaceMesh(MPI_Comm comm, PetscInt dim, const PetscInt faces[], const PetscReal lower[], const PetscReal upper[], PetscBool interpolate, DM *dm)
773 {
774   PetscInt  fac[3] = {1, 1, 1};
775   PetscReal low[3] = {0, 0, 0};
776   PetscReal upp[3] = {1, 1, 1};
777 
778   PetscFunctionBegin;
779   PetscCall(DMCreate(comm, dm));
780   PetscCall(DMSetType(*dm, DMPLEX));
781   PetscCall(DMPlexCreateBoxSurfaceMesh_Internal(*dm, dim, faces ? faces : fac, lower ? lower : low, upper ? upper : upp, interpolate));
782   PetscFunctionReturn(PETSC_SUCCESS);
783 }
784 
785 static PetscErrorCode DMPlexCreateLineMesh_Internal(DM dm, PetscInt segments, PetscReal lower, PetscReal upper, DMBoundaryType bd)
786 {
787   PetscInt     i, fStart, fEnd, numCells = 0, numVerts = 0;
788   PetscInt     numPoints[2], *coneSize, *cones, *coneOrientations;
789   PetscScalar *vertexCoords;
790   PetscReal    L, maxCell;
791   PetscBool    markerSeparate = PETSC_FALSE;
792   PetscInt     markerLeft = 1, faceMarkerLeft = 1;
793   PetscInt     markerRight = 1, faceMarkerRight = 2;
794   PetscBool    wrap = (bd == DM_BOUNDARY_PERIODIC || bd == DM_BOUNDARY_TWIST) ? PETSC_TRUE : PETSC_FALSE;
795   PetscMPIInt  rank;
796 
797   PetscFunctionBegin;
798   PetscAssertPointer(dm, 1);
799 
800   PetscCall(DMSetDimension(dm, 1));
801   PetscCall(DMCreateLabel(dm, "marker"));
802   PetscCall(DMCreateLabel(dm, "Face Sets"));
803 
804   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
805   if (rank == 0) numCells = segments;
806   if (rank == 0) numVerts = segments + (wrap ? 0 : 1);
807 
808   numPoints[0] = numVerts;
809   numPoints[1] = numCells;
810   PetscCall(PetscMalloc4(numCells + numVerts, &coneSize, numCells * 2, &cones, numCells + numVerts, &coneOrientations, numVerts, &vertexCoords));
811   PetscCall(PetscArrayzero(coneOrientations, numCells + numVerts));
812   for (i = 0; i < numCells; ++i) coneSize[i] = 2;
813   for (i = 0; i < numVerts; ++i) coneSize[numCells + i] = 0;
814   for (i = 0; i < numCells; ++i) {
815     cones[2 * i]     = numCells + i % numVerts;
816     cones[2 * i + 1] = numCells + (i + 1) % numVerts;
817   }
818   for (i = 0; i < numVerts; ++i) vertexCoords[i] = lower + (upper - lower) * ((PetscReal)i / (PetscReal)numCells);
819   PetscCall(DMPlexCreateFromDAG(dm, 1, numPoints, coneSize, cones, coneOrientations, vertexCoords));
820   PetscCall(PetscFree4(coneSize, cones, coneOrientations, vertexCoords));
821 
822   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_separate_marker", &markerSeparate, NULL));
823   if (markerSeparate) {
824     markerLeft  = faceMarkerLeft;
825     markerRight = faceMarkerRight;
826   }
827   if (!wrap && rank == 0) {
828     PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
829     PetscCall(DMSetLabelValue(dm, "marker", fStart, markerLeft));
830     PetscCall(DMSetLabelValue(dm, "marker", fEnd - 1, markerRight));
831     PetscCall(DMSetLabelValue(dm, "Face Sets", fStart, faceMarkerLeft));
832     PetscCall(DMSetLabelValue(dm, "Face Sets", fEnd - 1, faceMarkerRight));
833   }
834   if (wrap) {
835     L       = upper - lower;
836     maxCell = (PetscReal)1.1 * (L / (PetscReal)PetscMax(1, segments));
837     PetscCall(DMSetPeriodicity(dm, &maxCell, &lower, &L));
838   }
839   PetscCall(DMPlexSetRefinementUniform(dm, PETSC_TRUE));
840   PetscFunctionReturn(PETSC_SUCCESS);
841 }
842 
843 // Creates "Face Sets" label based on the standard box labeling conventions
844 static PetscErrorCode DMPlexSetBoxLabel_Internal(DM dm)
845 {
846   DMLabel         label;
847   IS              is;
848   PetscInt        dim, num_face;
849   const PetscInt *faces;
850   PetscInt        faceMarkerBottom, faceMarkerTop, faceMarkerFront, faceMarkerBack, faceMarkerRight, faceMarkerLeft;
851 
852   PetscFunctionBeginUser;
853   PetscCall(DMGetDimension(dm, &dim));
854   // Get Face Sets label
855   PetscCall(DMGetLabel(dm, "Face Sets", &label));
856   if (label) {
857     PetscCall(DMLabelReset(label));
858   } else {
859     PetscCall(DMCreateLabel(dm, "Face Sets"));
860     PetscCall(DMGetLabel(dm, "Face Sets", &label));
861   }
862   PetscCall(DMPlexMarkBoundaryFaces(dm, 1, label));
863   PetscCall(DMGetStratumIS(dm, "Face Sets", 1, &is));
864   if (!is) PetscFunctionReturn(PETSC_SUCCESS); // No faces on rank
865 
866   switch (dim) {
867   case 2:
868     faceMarkerTop    = 3;
869     faceMarkerBottom = 1;
870     faceMarkerRight  = 2;
871     faceMarkerLeft   = 4;
872     break;
873   case 3:
874     faceMarkerBottom = 1;
875     faceMarkerTop    = 2;
876     faceMarkerFront  = 3;
877     faceMarkerBack   = 4;
878     faceMarkerRight  = 5;
879     faceMarkerLeft   = 6;
880     break;
881   default:
882     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Dimension %" PetscInt_FMT " not supported", dim);
883   }
884 
885   PetscCall(ISGetLocalSize(is, &num_face));
886   PetscCall(ISGetIndices(is, &faces));
887   for (PetscInt f = 0; f < num_face; ++f) {
888     PetscInt  flip = 1, label_value = -1, face = faces[f];
889     PetscReal normal[3];
890 
891     { // Determine if orientation of face is flipped
892       PetscInt        num_cells_support, num_faces, start = -1;
893       const PetscInt *orients, *cell_faces, *cells;
894 
895       PetscCall(DMPlexGetSupport(dm, face, &cells));
896       PetscCall(DMPlexGetSupportSize(dm, face, &num_cells_support));
897       PetscCheck(num_cells_support == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Expected one cell in support of exterior face, but got %" PetscInt_FMT " cells", num_cells_support);
898       PetscCall(DMPlexGetCone(dm, cells[0], &cell_faces));
899       PetscCall(DMPlexGetConeSize(dm, cells[0], &num_faces));
900       for (PetscInt i = 0; i < num_faces; i++) {
901         if (cell_faces[i] == face) start = i;
902       }
903       PetscCheck(start >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_CORRUPT, "Could not find face %" PetscInt_FMT " in cone of its support", face);
904       PetscCall(DMPlexGetConeOrientation(dm, cells[0], &orients));
905       if (orients[start] < 0) flip = -1;
906     }
907 
908     PetscCall(DMPlexComputeCellGeometryFVM(dm, face, NULL, NULL, normal));
909     for (PetscInt i = 0; i < dim; i++) normal[i] *= flip;
910     switch (dim) {
911     case 2: {
912       if (PetscAbsReal(normal[0]) > PetscAbsReal(normal[1])) {
913         label_value = normal[0] > 0 ? faceMarkerRight : faceMarkerLeft;
914       } else {
915         label_value = normal[1] > 0 ? faceMarkerTop : faceMarkerBottom;
916       }
917     } break;
918     case 3: {
919       if (PetscAbsReal(normal[0]) > PetscAbsReal(normal[1])) {
920         if (PetscAbsReal(normal[0]) > PetscAbsReal(normal[2])) {
921           label_value = normal[0] > 0 ? faceMarkerRight : faceMarkerLeft;
922         } else {
923           label_value = normal[2] > 0 ? faceMarkerTop : faceMarkerBottom;
924         }
925       } else {
926         if (PetscAbsReal(normal[1]) > PetscAbsReal(normal[2])) {
927           label_value = normal[1] > 0 ? faceMarkerBack : faceMarkerFront;
928         } else {
929           label_value = normal[2] > 0 ? faceMarkerTop : faceMarkerBottom;
930         }
931       }
932     } break;
933     }
934 
935     PetscInt previous_label_value; // always 1 due to DMPlexMarkBoundaryFaces call above
936     PetscCall(DMGetLabelValue(dm, "Face Sets", face, &previous_label_value));
937     PetscCall(DMClearLabelValue(dm, "Face Sets", face, previous_label_value));
938     PetscCall(DMSetLabelValue(dm, "Face Sets", face, label_value));
939   }
940   PetscCall(ISRestoreIndices(is, &faces));
941   PetscCall(ISDestroy(&is));
942   PetscFunctionReturn(PETSC_SUCCESS);
943 }
944 
945 static PetscErrorCode DMPlexCreateBoxMesh_Simplex_Internal(DM dm, PetscInt dim, const PetscInt faces[], const PetscReal lower[], const PetscReal upper[], const DMBoundaryType periodicity[], PetscBool interpolate)
946 {
947   DM      boundary, vol;
948   DMLabel bdlabel;
949 
950   PetscFunctionBegin;
951   PetscAssertPointer(dm, 1);
952   for (PetscInt i = 0; i < dim; ++i) PetscCheck(periodicity[i] == DM_BOUNDARY_NONE, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Periodicity is not supported for simplex meshes");
953   PetscCall(DMCreate(PetscObjectComm((PetscObject)dm), &boundary));
954   PetscCall(DMSetType(boundary, DMPLEX));
955   PetscCall(DMPlexCreateBoxSurfaceMesh_Internal(boundary, dim, faces, lower, upper, PETSC_FALSE));
956   PetscCall(DMPlexGenerate(boundary, NULL, interpolate, &vol));
957   PetscCall(DMGetLabel(vol, "marker", &bdlabel));
958   if (bdlabel) PetscCall(DMPlexLabelComplete(vol, bdlabel));
959   PetscCall(DMPlexCopy_Internal(dm, PETSC_TRUE, PETSC_FALSE, vol));
960   PetscCall(DMPlexReplace_Internal(dm, &vol));
961   if (interpolate) {
962     PetscCall(DMPlexInterpolateInPlace_Internal(dm));
963     PetscCall(DMPlexSetBoxLabel_Internal(dm));
964   }
965   PetscCall(DMDestroy(&boundary));
966   PetscFunctionReturn(PETSC_SUCCESS);
967 }
968 
969 static PetscErrorCode DMPlexCreateCubeMesh_Internal(DM dm, const PetscReal lower[], const PetscReal upper[], const PetscInt edges[], DMBoundaryType bdX, DMBoundaryType bdY, DMBoundaryType bdZ)
970 {
971   DMLabel     cutLabel  = NULL;
972   PetscInt    markerTop = 1, faceMarkerTop = 1;
973   PetscInt    markerBottom = 1, faceMarkerBottom = 1;
974   PetscInt    markerFront = 1, faceMarkerFront = 1;
975   PetscInt    markerBack = 1, faceMarkerBack = 1;
976   PetscInt    markerRight = 1, faceMarkerRight = 1;
977   PetscInt    markerLeft = 1, faceMarkerLeft = 1;
978   PetscInt    dim;
979   PetscBool   markerSeparate = PETSC_FALSE, cutMarker = PETSC_FALSE;
980   PetscMPIInt rank;
981 
982   PetscFunctionBegin;
983   PetscCall(DMGetDimension(dm, &dim));
984   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
985   PetscCall(DMCreateLabel(dm, "marker"));
986   PetscCall(DMCreateLabel(dm, "Face Sets"));
987   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_periodic_cut", &cutMarker, NULL));
988   if (bdX == DM_BOUNDARY_PERIODIC || bdX == DM_BOUNDARY_TWIST || bdY == DM_BOUNDARY_PERIODIC || bdY == DM_BOUNDARY_TWIST || bdZ == DM_BOUNDARY_PERIODIC || bdZ == DM_BOUNDARY_TWIST) {
989     if (cutMarker) {
990       PetscCall(DMCreateLabel(dm, "periodic_cut"));
991       PetscCall(DMGetLabel(dm, "periodic_cut", &cutLabel));
992     }
993   }
994   switch (dim) {
995   case 2:
996     faceMarkerTop    = 3;
997     faceMarkerBottom = 1;
998     faceMarkerRight  = 2;
999     faceMarkerLeft   = 4;
1000     break;
1001   case 3:
1002     faceMarkerBottom = 1;
1003     faceMarkerTop    = 2;
1004     faceMarkerFront  = 3;
1005     faceMarkerBack   = 4;
1006     faceMarkerRight  = 5;
1007     faceMarkerLeft   = 6;
1008     break;
1009   default:
1010     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Dimension %" PetscInt_FMT " not supported", dim);
1011   }
1012   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_separate_marker", &markerSeparate, NULL));
1013   if (markerSeparate) {
1014     markerBottom = faceMarkerBottom;
1015     markerTop    = faceMarkerTop;
1016     markerFront  = faceMarkerFront;
1017     markerBack   = faceMarkerBack;
1018     markerRight  = faceMarkerRight;
1019     markerLeft   = faceMarkerLeft;
1020   }
1021   {
1022     const PetscInt numXEdges    = rank == 0 ? edges[0] : 0;
1023     const PetscInt numYEdges    = rank == 0 ? edges[1] : 0;
1024     const PetscInt numZEdges    = rank == 0 ? edges[2] : 0;
1025     const PetscInt numXVertices = rank == 0 ? (bdX == DM_BOUNDARY_PERIODIC || bdX == DM_BOUNDARY_TWIST ? edges[0] : edges[0] + 1) : 0;
1026     const PetscInt numYVertices = rank == 0 ? (bdY == DM_BOUNDARY_PERIODIC || bdY == DM_BOUNDARY_TWIST ? edges[1] : edges[1] + 1) : 0;
1027     const PetscInt numZVertices = rank == 0 ? (bdZ == DM_BOUNDARY_PERIODIC || bdZ == DM_BOUNDARY_TWIST ? edges[2] : edges[2] + 1) : 0;
1028     const PetscInt numCells     = numXEdges * numYEdges * numZEdges;
1029     const PetscInt numXFaces    = numYEdges * numZEdges;
1030     const PetscInt numYFaces    = numXEdges * numZEdges;
1031     const PetscInt numZFaces    = numXEdges * numYEdges;
1032     const PetscInt numTotXFaces = numXVertices * numXFaces;
1033     const PetscInt numTotYFaces = numYVertices * numYFaces;
1034     const PetscInt numTotZFaces = numZVertices * numZFaces;
1035     const PetscInt numFaces     = numTotXFaces + numTotYFaces + numTotZFaces;
1036     const PetscInt numTotXEdges = numXEdges * numYVertices * numZVertices;
1037     const PetscInt numTotYEdges = numYEdges * numXVertices * numZVertices;
1038     const PetscInt numTotZEdges = numZEdges * numXVertices * numYVertices;
1039     const PetscInt numVertices  = numXVertices * numYVertices * numZVertices;
1040     const PetscInt numEdges     = numTotXEdges + numTotYEdges + numTotZEdges;
1041     const PetscInt firstVertex  = (dim == 2) ? numFaces : numCells;
1042     const PetscInt firstXFace   = (dim == 2) ? 0 : numCells + numVertices;
1043     const PetscInt firstYFace   = firstXFace + numTotXFaces;
1044     const PetscInt firstZFace   = firstYFace + numTotYFaces;
1045     const PetscInt firstXEdge   = numCells + numFaces + numVertices;
1046     const PetscInt firstYEdge   = firstXEdge + numTotXEdges;
1047     const PetscInt firstZEdge   = firstYEdge + numTotYEdges;
1048     Vec            coordinates;
1049     PetscSection   coordSection;
1050     PetscScalar   *coords;
1051     PetscInt       coordSize;
1052     PetscInt       v, vx, vy, vz;
1053     PetscInt       c, f, fx, fy, fz, e, ex, ey, ez;
1054 
1055     PetscCall(DMPlexSetChart(dm, 0, numCells + numFaces + numEdges + numVertices));
1056     for (c = 0; c < numCells; c++) PetscCall(DMPlexSetConeSize(dm, c, 6));
1057     for (f = firstXFace; f < firstXFace + numFaces; ++f) PetscCall(DMPlexSetConeSize(dm, f, 4));
1058     for (e = firstXEdge; e < firstXEdge + numEdges; ++e) PetscCall(DMPlexSetConeSize(dm, e, 2));
1059     PetscCall(DMSetUp(dm)); /* Allocate space for cones */
1060     /* Build cells */
1061     for (fz = 0; fz < numZEdges; ++fz) {
1062       for (fy = 0; fy < numYEdges; ++fy) {
1063         for (fx = 0; fx < numXEdges; ++fx) {
1064           PetscInt cell  = (fz * numYEdges + fy) * numXEdges + fx;
1065           PetscInt faceB = firstZFace + (fy * numXEdges + fx) * numZVertices + fz;
1066           PetscInt faceT = firstZFace + (fy * numXEdges + fx) * numZVertices + ((fz + 1) % numZVertices);
1067           PetscInt faceF = firstYFace + (fz * numXEdges + fx) * numYVertices + fy;
1068           PetscInt faceK = firstYFace + (fz * numXEdges + fx) * numYVertices + ((fy + 1) % numYVertices);
1069           PetscInt faceL = firstXFace + (fz * numYEdges + fy) * numXVertices + fx;
1070           PetscInt faceR = firstXFace + (fz * numYEdges + fy) * numXVertices + ((fx + 1) % numXVertices);
1071           /* B,  T,  F,  K,  R,  L */
1072           PetscInt ornt[6] = {-2, 0, 0, -3, 0, -2}; /* ??? */
1073           PetscInt cone[6];
1074 
1075           /* no boundary twisting in 3D */
1076           cone[0] = faceB;
1077           cone[1] = faceT;
1078           cone[2] = faceF;
1079           cone[3] = faceK;
1080           cone[4] = faceR;
1081           cone[5] = faceL;
1082           PetscCall(DMPlexSetCone(dm, cell, cone));
1083           PetscCall(DMPlexSetConeOrientation(dm, cell, ornt));
1084           if (bdX != DM_BOUNDARY_NONE && fx == numXEdges - 1 && cutLabel) PetscCall(DMLabelSetValue(cutLabel, cell, 2));
1085           if (bdY != DM_BOUNDARY_NONE && fy == numYEdges - 1 && cutLabel) PetscCall(DMLabelSetValue(cutLabel, cell, 2));
1086           if (bdZ != DM_BOUNDARY_NONE && fz == numZEdges - 1 && cutLabel) PetscCall(DMLabelSetValue(cutLabel, cell, 2));
1087         }
1088       }
1089     }
1090     /* Build x faces */
1091     for (fz = 0; fz < numZEdges; ++fz) {
1092       for (fy = 0; fy < numYEdges; ++fy) {
1093         for (fx = 0; fx < numXVertices; ++fx) {
1094           PetscInt face    = firstXFace + (fz * numYEdges + fy) * numXVertices + fx;
1095           PetscInt edgeL   = firstZEdge + (fy * numXVertices + fx) * numZEdges + fz;
1096           PetscInt edgeR   = firstZEdge + (((fy + 1) % numYVertices) * numXVertices + fx) * numZEdges + fz;
1097           PetscInt edgeB   = firstYEdge + (fz * numXVertices + fx) * numYEdges + fy;
1098           PetscInt edgeT   = firstYEdge + (((fz + 1) % numZVertices) * numXVertices + fx) * numYEdges + fy;
1099           PetscInt ornt[4] = {0, 0, -1, -1};
1100           PetscInt cone[4];
1101 
1102           if (dim == 3) {
1103             /* markers */
1104             if (bdX != DM_BOUNDARY_PERIODIC) {
1105               if (fx == numXVertices - 1) {
1106                 PetscCall(DMSetLabelValue(dm, "Face Sets", face, faceMarkerRight));
1107                 PetscCall(DMSetLabelValue(dm, "marker", face, markerRight));
1108               } else if (fx == 0) {
1109                 PetscCall(DMSetLabelValue(dm, "Face Sets", face, faceMarkerLeft));
1110                 PetscCall(DMSetLabelValue(dm, "marker", face, markerLeft));
1111               }
1112             }
1113           }
1114           cone[0] = edgeB;
1115           cone[1] = edgeR;
1116           cone[2] = edgeT;
1117           cone[3] = edgeL;
1118           PetscCall(DMPlexSetCone(dm, face, cone));
1119           PetscCall(DMPlexSetConeOrientation(dm, face, ornt));
1120         }
1121       }
1122     }
1123     /* Build y faces */
1124     for (fz = 0; fz < numZEdges; ++fz) {
1125       for (fx = 0; fx < numXEdges; ++fx) {
1126         for (fy = 0; fy < numYVertices; ++fy) {
1127           PetscInt face    = firstYFace + (fz * numXEdges + fx) * numYVertices + fy;
1128           PetscInt edgeL   = firstZEdge + (fy * numXVertices + fx) * numZEdges + fz;
1129           PetscInt edgeR   = firstZEdge + (fy * numXVertices + ((fx + 1) % numXVertices)) * numZEdges + fz;
1130           PetscInt edgeB   = firstXEdge + (fz * numYVertices + fy) * numXEdges + fx;
1131           PetscInt edgeT   = firstXEdge + (((fz + 1) % numZVertices) * numYVertices + fy) * numXEdges + fx;
1132           PetscInt ornt[4] = {0, 0, -1, -1};
1133           PetscInt cone[4];
1134 
1135           if (dim == 3) {
1136             /* markers */
1137             if (bdY != DM_BOUNDARY_PERIODIC) {
1138               if (fy == numYVertices - 1) {
1139                 PetscCall(DMSetLabelValue(dm, "Face Sets", face, faceMarkerBack));
1140                 PetscCall(DMSetLabelValue(dm, "marker", face, markerBack));
1141               } else if (fy == 0) {
1142                 PetscCall(DMSetLabelValue(dm, "Face Sets", face, faceMarkerFront));
1143                 PetscCall(DMSetLabelValue(dm, "marker", face, markerFront));
1144               }
1145             }
1146           }
1147           cone[0] = edgeB;
1148           cone[1] = edgeR;
1149           cone[2] = edgeT;
1150           cone[3] = edgeL;
1151           PetscCall(DMPlexSetCone(dm, face, cone));
1152           PetscCall(DMPlexSetConeOrientation(dm, face, ornt));
1153         }
1154       }
1155     }
1156     /* Build z faces */
1157     for (fy = 0; fy < numYEdges; ++fy) {
1158       for (fx = 0; fx < numXEdges; ++fx) {
1159         for (fz = 0; fz < numZVertices; fz++) {
1160           PetscInt face    = firstZFace + (fy * numXEdges + fx) * numZVertices + fz;
1161           PetscInt edgeL   = firstYEdge + (fz * numXVertices + fx) * numYEdges + fy;
1162           PetscInt edgeR   = firstYEdge + (fz * numXVertices + ((fx + 1) % numXVertices)) * numYEdges + fy;
1163           PetscInt edgeB   = firstXEdge + (fz * numYVertices + fy) * numXEdges + fx;
1164           PetscInt edgeT   = firstXEdge + (fz * numYVertices + ((fy + 1) % numYVertices)) * numXEdges + fx;
1165           PetscInt ornt[4] = {0, 0, -1, -1};
1166           PetscInt cone[4];
1167 
1168           if (dim == 2) {
1169             if (bdX == DM_BOUNDARY_TWIST && fx == numXEdges - 1) {
1170               edgeR += numYEdges - 1 - 2 * fy;
1171               ornt[1] = -1;
1172             }
1173             if (bdY == DM_BOUNDARY_TWIST && fy == numYEdges - 1) {
1174               edgeT += numXEdges - 1 - 2 * fx;
1175               ornt[2] = 0;
1176             }
1177             if (bdX != DM_BOUNDARY_NONE && fx == numXEdges - 1 && cutLabel) PetscCall(DMLabelSetValue(cutLabel, face, 2));
1178             if (bdY != DM_BOUNDARY_NONE && fy == numYEdges - 1 && cutLabel) PetscCall(DMLabelSetValue(cutLabel, face, 2));
1179           } else {
1180             /* markers */
1181             if (bdZ != DM_BOUNDARY_PERIODIC) {
1182               if (fz == numZVertices - 1) {
1183                 PetscCall(DMSetLabelValue(dm, "Face Sets", face, faceMarkerTop));
1184                 PetscCall(DMSetLabelValue(dm, "marker", face, markerTop));
1185               } else if (fz == 0) {
1186                 PetscCall(DMSetLabelValue(dm, "Face Sets", face, faceMarkerBottom));
1187                 PetscCall(DMSetLabelValue(dm, "marker", face, markerBottom));
1188               }
1189             }
1190           }
1191           cone[0] = edgeB;
1192           cone[1] = edgeR;
1193           cone[2] = edgeT;
1194           cone[3] = edgeL;
1195           PetscCall(DMPlexSetCone(dm, face, cone));
1196           PetscCall(DMPlexSetConeOrientation(dm, face, ornt));
1197         }
1198       }
1199     }
1200     /* Build Z edges*/
1201     for (vy = 0; vy < numYVertices; vy++) {
1202       for (vx = 0; vx < numXVertices; vx++) {
1203         for (ez = 0; ez < numZEdges; ez++) {
1204           const PetscInt edge    = firstZEdge + (vy * numXVertices + vx) * numZEdges + ez;
1205           const PetscInt vertexB = firstVertex + (ez * numYVertices + vy) * numXVertices + vx;
1206           const PetscInt vertexT = firstVertex + (((ez + 1) % numZVertices) * numYVertices + vy) * numXVertices + vx;
1207           PetscInt       cone[2];
1208 
1209           cone[0] = vertexB;
1210           cone[1] = vertexT;
1211           PetscCall(DMPlexSetCone(dm, edge, cone));
1212           if (dim == 3) {
1213             if (bdX != DM_BOUNDARY_PERIODIC) {
1214               if (vx == numXVertices - 1) {
1215                 PetscCall(DMSetLabelValue(dm, "marker", edge, markerRight));
1216                 PetscCall(DMSetLabelValue(dm, "marker", cone[0], markerRight));
1217                 if (ez == numZEdges - 1) PetscCall(DMSetLabelValue(dm, "marker", cone[1], markerRight));
1218               } else if (vx == 0) {
1219                 PetscCall(DMSetLabelValue(dm, "marker", edge, markerLeft));
1220                 PetscCall(DMSetLabelValue(dm, "marker", cone[0], markerLeft));
1221                 if (ez == numZEdges - 1) PetscCall(DMSetLabelValue(dm, "marker", cone[1], markerLeft));
1222               }
1223             }
1224             if (bdY != DM_BOUNDARY_PERIODIC) {
1225               if (vy == numYVertices - 1) {
1226                 PetscCall(DMSetLabelValue(dm, "marker", edge, markerBack));
1227                 PetscCall(DMSetLabelValue(dm, "marker", cone[0], markerBack));
1228                 if (ez == numZEdges - 1) PetscCall(DMSetLabelValue(dm, "marker", cone[1], markerBack));
1229               } else if (vy == 0) {
1230                 PetscCall(DMSetLabelValue(dm, "marker", edge, markerFront));
1231                 PetscCall(DMSetLabelValue(dm, "marker", cone[0], markerFront));
1232                 if (ez == numZEdges - 1) PetscCall(DMSetLabelValue(dm, "marker", cone[1], markerFront));
1233               }
1234             }
1235           }
1236         }
1237       }
1238     }
1239     /* Build Y edges*/
1240     for (vz = 0; vz < numZVertices; vz++) {
1241       for (vx = 0; vx < numXVertices; vx++) {
1242         for (ey = 0; ey < numYEdges; ey++) {
1243           const PetscInt nextv   = (dim == 2 && bdY == DM_BOUNDARY_TWIST && ey == numYEdges - 1) ? (numXVertices - vx - 1) : (vz * numYVertices + ((ey + 1) % numYVertices)) * numXVertices + vx;
1244           const PetscInt edge    = firstYEdge + (vz * numXVertices + vx) * numYEdges + ey;
1245           const PetscInt vertexF = firstVertex + (vz * numYVertices + ey) * numXVertices + vx;
1246           const PetscInt vertexK = firstVertex + nextv;
1247           PetscInt       cone[2];
1248 
1249           cone[0] = vertexF;
1250           cone[1] = vertexK;
1251           PetscCall(DMPlexSetCone(dm, edge, cone));
1252           if (dim == 2) {
1253             if ((bdX != DM_BOUNDARY_PERIODIC) && (bdX != DM_BOUNDARY_TWIST)) {
1254               if (vx == numXVertices - 1) {
1255                 PetscCall(DMSetLabelValue(dm, "Face Sets", edge, faceMarkerRight));
1256                 PetscCall(DMSetLabelValue(dm, "marker", edge, markerRight));
1257                 PetscCall(DMSetLabelValue(dm, "marker", cone[0], markerRight));
1258                 if (ey == numYEdges - 1) PetscCall(DMSetLabelValue(dm, "marker", cone[1], markerRight));
1259               } else if (vx == 0) {
1260                 PetscCall(DMSetLabelValue(dm, "Face Sets", edge, faceMarkerLeft));
1261                 PetscCall(DMSetLabelValue(dm, "marker", edge, markerLeft));
1262                 PetscCall(DMSetLabelValue(dm, "marker", cone[0], markerLeft));
1263                 if (ey == numYEdges - 1) PetscCall(DMSetLabelValue(dm, "marker", cone[1], markerLeft));
1264               }
1265             } else {
1266               if (vx == 0 && cutLabel) {
1267                 PetscCall(DMLabelSetValue(cutLabel, edge, 1));
1268                 PetscCall(DMLabelSetValue(cutLabel, cone[0], 1));
1269                 if (ey == numYEdges - 1) PetscCall(DMLabelSetValue(cutLabel, cone[1], 1));
1270               }
1271             }
1272           } else {
1273             if (bdX != DM_BOUNDARY_PERIODIC) {
1274               if (vx == numXVertices - 1) {
1275                 PetscCall(DMSetLabelValue(dm, "marker", edge, markerRight));
1276                 PetscCall(DMSetLabelValue(dm, "marker", cone[0], markerRight));
1277                 if (ey == numYEdges - 1) PetscCall(DMSetLabelValue(dm, "marker", cone[1], markerRight));
1278               } else if (vx == 0) {
1279                 PetscCall(DMSetLabelValue(dm, "marker", edge, markerLeft));
1280                 PetscCall(DMSetLabelValue(dm, "marker", cone[0], markerLeft));
1281                 if (ey == numYEdges - 1) PetscCall(DMSetLabelValue(dm, "marker", cone[1], markerLeft));
1282               }
1283             }
1284             if (bdZ != DM_BOUNDARY_PERIODIC) {
1285               if (vz == numZVertices - 1) {
1286                 PetscCall(DMSetLabelValue(dm, "marker", edge, markerTop));
1287                 PetscCall(DMSetLabelValue(dm, "marker", cone[0], markerTop));
1288                 if (ey == numYEdges - 1) PetscCall(DMSetLabelValue(dm, "marker", cone[1], markerTop));
1289               } else if (vz == 0) {
1290                 PetscCall(DMSetLabelValue(dm, "marker", edge, markerBottom));
1291                 PetscCall(DMSetLabelValue(dm, "marker", cone[0], markerBottom));
1292                 if (ey == numYEdges - 1) PetscCall(DMSetLabelValue(dm, "marker", cone[1], markerBottom));
1293               }
1294             }
1295           }
1296         }
1297       }
1298     }
1299     /* Build X edges*/
1300     for (vz = 0; vz < numZVertices; vz++) {
1301       for (vy = 0; vy < numYVertices; vy++) {
1302         for (ex = 0; ex < numXEdges; ex++) {
1303           const PetscInt nextv   = (dim == 2 && bdX == DM_BOUNDARY_TWIST && ex == numXEdges - 1) ? (numYVertices - vy - 1) * numXVertices : (vz * numYVertices + vy) * numXVertices + (ex + 1) % numXVertices;
1304           const PetscInt edge    = firstXEdge + (vz * numYVertices + vy) * numXEdges + ex;
1305           const PetscInt vertexL = firstVertex + (vz * numYVertices + vy) * numXVertices + ex;
1306           const PetscInt vertexR = firstVertex + nextv;
1307           PetscInt       cone[2];
1308 
1309           cone[0] = vertexL;
1310           cone[1] = vertexR;
1311           PetscCall(DMPlexSetCone(dm, edge, cone));
1312           if (dim == 2) {
1313             if ((bdY != DM_BOUNDARY_PERIODIC) && (bdY != DM_BOUNDARY_TWIST)) {
1314               if (vy == numYVertices - 1) {
1315                 PetscCall(DMSetLabelValue(dm, "Face Sets", edge, faceMarkerTop));
1316                 PetscCall(DMSetLabelValue(dm, "marker", edge, markerTop));
1317                 PetscCall(DMSetLabelValue(dm, "marker", cone[0], markerTop));
1318                 if (ex == numXEdges - 1) PetscCall(DMSetLabelValue(dm, "marker", cone[1], markerTop));
1319               } else if (vy == 0) {
1320                 PetscCall(DMSetLabelValue(dm, "Face Sets", edge, faceMarkerBottom));
1321                 PetscCall(DMSetLabelValue(dm, "marker", edge, markerBottom));
1322                 PetscCall(DMSetLabelValue(dm, "marker", cone[0], markerBottom));
1323                 if (ex == numXEdges - 1) PetscCall(DMSetLabelValue(dm, "marker", cone[1], markerBottom));
1324               }
1325             } else {
1326               if (vy == 0 && cutLabel) {
1327                 PetscCall(DMLabelSetValue(cutLabel, edge, 1));
1328                 PetscCall(DMLabelSetValue(cutLabel, cone[0], 1));
1329                 if (ex == numXEdges - 1) PetscCall(DMLabelSetValue(cutLabel, cone[1], 1));
1330               }
1331             }
1332           } else {
1333             if (bdY != DM_BOUNDARY_PERIODIC) {
1334               if (vy == numYVertices - 1) {
1335                 PetscCall(DMSetLabelValue(dm, "marker", edge, markerBack));
1336                 PetscCall(DMSetLabelValue(dm, "marker", cone[0], markerBack));
1337                 if (ex == numXEdges - 1) PetscCall(DMSetLabelValue(dm, "marker", cone[1], markerBack));
1338               } else if (vy == 0) {
1339                 PetscCall(DMSetLabelValue(dm, "marker", edge, markerFront));
1340                 PetscCall(DMSetLabelValue(dm, "marker", cone[0], markerFront));
1341                 if (ex == numXEdges - 1) PetscCall(DMSetLabelValue(dm, "marker", cone[1], markerFront));
1342               }
1343             }
1344             if (bdZ != DM_BOUNDARY_PERIODIC) {
1345               if (vz == numZVertices - 1) {
1346                 PetscCall(DMSetLabelValue(dm, "marker", edge, markerTop));
1347                 PetscCall(DMSetLabelValue(dm, "marker", cone[0], markerTop));
1348                 if (ex == numXEdges - 1) PetscCall(DMSetLabelValue(dm, "marker", cone[1], markerTop));
1349               } else if (vz == 0) {
1350                 PetscCall(DMSetLabelValue(dm, "marker", edge, markerBottom));
1351                 PetscCall(DMSetLabelValue(dm, "marker", cone[0], markerBottom));
1352                 if (ex == numXEdges - 1) PetscCall(DMSetLabelValue(dm, "marker", cone[1], markerBottom));
1353               }
1354             }
1355           }
1356         }
1357       }
1358     }
1359     PetscCall(DMPlexSymmetrize(dm));
1360     PetscCall(DMPlexStratify(dm));
1361     /* Build coordinates */
1362     PetscCall(DMGetCoordinateSection(dm, &coordSection));
1363     PetscCall(PetscSectionSetNumFields(coordSection, 1));
1364     PetscCall(PetscSectionSetFieldComponents(coordSection, 0, dim));
1365     PetscCall(PetscSectionSetChart(coordSection, firstVertex, firstVertex + numVertices));
1366     for (v = firstVertex; v < firstVertex + numVertices; ++v) {
1367       PetscCall(PetscSectionSetDof(coordSection, v, dim));
1368       PetscCall(PetscSectionSetFieldDof(coordSection, v, 0, dim));
1369     }
1370     PetscCall(PetscSectionSetUp(coordSection));
1371     PetscCall(PetscSectionGetStorageSize(coordSection, &coordSize));
1372     PetscCall(VecCreate(PETSC_COMM_SELF, &coordinates));
1373     PetscCall(PetscObjectSetName((PetscObject)coordinates, "coordinates"));
1374     PetscCall(VecSetSizes(coordinates, coordSize, PETSC_DETERMINE));
1375     PetscCall(VecSetBlockSize(coordinates, dim));
1376     PetscCall(VecSetType(coordinates, VECSTANDARD));
1377     PetscCall(VecGetArray(coordinates, &coords));
1378     for (vz = 0; vz < numZVertices; ++vz) {
1379       for (vy = 0; vy < numYVertices; ++vy) {
1380         for (vx = 0; vx < numXVertices; ++vx) {
1381           coords[((vz * numYVertices + vy) * numXVertices + vx) * dim + 0] = lower[0] + ((upper[0] - lower[0]) / numXEdges) * vx;
1382           coords[((vz * numYVertices + vy) * numXVertices + vx) * dim + 1] = lower[1] + ((upper[1] - lower[1]) / numYEdges) * vy;
1383           if (dim == 3) coords[((vz * numYVertices + vy) * numXVertices + vx) * dim + 2] = lower[2] + ((upper[2] - lower[2]) / numZEdges) * vz;
1384         }
1385       }
1386     }
1387     PetscCall(VecRestoreArray(coordinates, &coords));
1388     PetscCall(DMSetCoordinatesLocal(dm, coordinates));
1389     PetscCall(VecDestroy(&coordinates));
1390   }
1391   PetscFunctionReturn(PETSC_SUCCESS);
1392 }
1393 
1394 static PetscErrorCode DMPlexCreateBoxMesh_Tensor_Internal(DM dm, PetscInt dim, const PetscInt faces[], const PetscReal lower[], const PetscReal upper[], const DMBoundaryType periodicity[])
1395 {
1396   DMBoundaryType bdt[3] = {DM_BOUNDARY_NONE, DM_BOUNDARY_NONE, DM_BOUNDARY_NONE};
1397   PetscInt       fac[3] = {0, 0, 0}, d;
1398 
1399   PetscFunctionBegin;
1400   PetscAssertPointer(dm, 1);
1401   PetscValidLogicalCollectiveInt(dm, dim, 2);
1402   PetscCall(DMSetDimension(dm, dim));
1403   for (d = 0; d < dim; ++d) {
1404     fac[d] = faces[d];
1405     bdt[d] = periodicity[d];
1406   }
1407   PetscCall(DMPlexCreateCubeMesh_Internal(dm, lower, upper, fac, bdt[0], bdt[1], bdt[2]));
1408   if (periodicity[0] == DM_BOUNDARY_PERIODIC || periodicity[0] == DM_BOUNDARY_TWIST || periodicity[1] == DM_BOUNDARY_PERIODIC || periodicity[1] == DM_BOUNDARY_TWIST || (dim > 2 && (periodicity[2] == DM_BOUNDARY_PERIODIC || periodicity[2] == DM_BOUNDARY_TWIST))) {
1409     PetscReal L[3]       = {-1., -1., 0.};
1410     PetscReal maxCell[3] = {-1., -1., 0.};
1411 
1412     for (d = 0; d < dim; ++d) {
1413       if (periodicity[d] != DM_BOUNDARY_NONE) {
1414         L[d]       = upper[d] - lower[d];
1415         maxCell[d] = 1.1 * (L[d] / PetscMax(1, faces[d]));
1416       }
1417     }
1418     PetscCall(DMSetPeriodicity(dm, maxCell, lower, L));
1419   }
1420   PetscCall(DMPlexSetRefinementUniform(dm, PETSC_TRUE));
1421   PetscFunctionReturn(PETSC_SUCCESS);
1422 }
1423 
1424 static PetscErrorCode DMPlexCreateBoxMesh_Internal(DM dm, DMPlexShape shape, PetscInt dim, PetscBool simplex, const PetscInt faces[], const PetscReal lower[], const PetscReal upper[], const DMBoundaryType periodicity[], PetscBool interpolate)
1425 {
1426   PetscFunctionBegin;
1427   PetscCall(PetscLogEventBegin(DMPLEX_Generate, dm, 0, 0, 0));
1428   if (shape == DM_SHAPE_ZBOX) PetscCall(DMPlexCreateBoxMesh_Tensor_SFC_Internal(dm, dim, faces, lower, upper, periodicity, interpolate));
1429   else if (dim == 1) PetscCall(DMPlexCreateLineMesh_Internal(dm, faces[0], lower[0], upper[0], periodicity[0]));
1430   else if (simplex) PetscCall(DMPlexCreateBoxMesh_Simplex_Internal(dm, dim, faces, lower, upper, periodicity, interpolate));
1431   else PetscCall(DMPlexCreateBoxMesh_Tensor_Internal(dm, dim, faces, lower, upper, periodicity));
1432   if (!interpolate && dim > 1 && !simplex) {
1433     DM udm;
1434 
1435     PetscCall(DMPlexUninterpolate(dm, &udm));
1436     PetscCall(DMPlexCopyCoordinates(dm, udm));
1437     PetscCall(DMPlexReplace_Internal(dm, &udm));
1438   }
1439   PetscCall(PetscLogEventEnd(DMPLEX_Generate, dm, 0, 0, 0));
1440   PetscFunctionReturn(PETSC_SUCCESS);
1441 }
1442 
1443 /*@
1444   DMPlexCreateBoxMesh - Creates a mesh on the tensor product of unit intervals (box) using simplices or tensor cells (hexahedra).
1445 
1446   Collective
1447 
1448   Input Parameters:
1449 + comm        - The communicator for the `DM` object
1450 . dim         - The spatial dimension
1451 . simplex     - `PETSC_TRUE` for simplices, `PETSC_FALSE` for tensor cells
1452 . faces       - Number of faces per dimension, or `NULL` for (1,) in 1D and (2, 2) in 2D and (1, 1, 1) in 3D
1453 . lower       - The lower left corner, or `NULL` for (0, 0, 0)
1454 . upper       - The upper right corner, or `NULL` for (1, 1, 1)
1455 . periodicity - The boundary type for the X,Y,Z direction, or `NULL` for `DM_BOUNDARY_NONE`
1456 - interpolate - Flag to create intermediate mesh pieces (edges, faces)
1457 
1458   Output Parameter:
1459 . dm - The `DM` object
1460 
1461   Level: beginner
1462 
1463   Note:
1464   To customize this mesh using options, use
1465 .vb
1466   DMCreate(comm, &dm);
1467   DMSetType(dm, DMPLEX);
1468   DMSetFromOptions(dm);
1469 .ve
1470   and use the options in `DMSetFromOptions()`.
1471 
1472   Here is the numbering returned for 2 faces in each direction for tensor cells\:
1473 .vb
1474  10---17---11---18----12
1475   |         |         |
1476   |         |         |
1477  20    2   22    3    24
1478   |         |         |
1479   |         |         |
1480   7---15----8---16----9
1481   |         |         |
1482   |         |         |
1483  19    0   21    1   23
1484   |         |         |
1485   |         |         |
1486   4---13----5---14----6
1487 .ve
1488   and for simplicial cells
1489 .vb
1490  14----8---15----9----16
1491   |\     5  |\      7 |
1492   | \       | \       |
1493  13   2    14    3    15
1494   | 4   \   | 6   \   |
1495   |       \ |       \ |
1496  11----6---12----7----13
1497   |\        |\        |
1498   | \    1  | \     3 |
1499  10   0    11    1    12
1500   | 0   \   | 2   \   |
1501   |       \ |       \ |
1502   8----4----9----5----10
1503 .ve
1504 
1505 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreateFromFile()`, `DMPlexCreateHexCylinderMesh()`, `DMSetType()`, `DMCreate()`
1506 @*/
1507 PetscErrorCode DMPlexCreateBoxMesh(MPI_Comm comm, PetscInt dim, PetscBool simplex, const PetscInt faces[], const PetscReal lower[], const PetscReal upper[], const DMBoundaryType periodicity[], PetscBool interpolate, DM *dm)
1508 {
1509   PetscInt       fac[3] = {1, 1, 1};
1510   PetscReal      low[3] = {0, 0, 0};
1511   PetscReal      upp[3] = {1, 1, 1};
1512   DMBoundaryType bdt[3] = {DM_BOUNDARY_NONE, DM_BOUNDARY_NONE, DM_BOUNDARY_NONE};
1513 
1514   PetscFunctionBegin;
1515   PetscCall(DMCreate(comm, dm));
1516   PetscCall(DMSetType(*dm, DMPLEX));
1517   PetscCall(DMPlexCreateBoxMesh_Internal(*dm, DM_SHAPE_BOX, dim, simplex, faces ? faces : fac, lower ? lower : low, upper ? upper : upp, periodicity ? periodicity : bdt, interpolate));
1518   if (periodicity) PetscCall(DMLocalizeCoordinates(*dm));
1519   PetscFunctionReturn(PETSC_SUCCESS);
1520 }
1521 
1522 static PetscErrorCode DMPlexCreateWedgeBoxMesh_Internal(DM dm, const PetscInt faces[], const PetscReal lower[], const PetscReal upper[], const DMBoundaryType periodicity[])
1523 {
1524   DM       bdm, vol;
1525   PetscInt i;
1526 
1527   PetscFunctionBegin;
1528   // TODO Now we can support periodicity
1529   for (i = 0; i < 3; ++i) PetscCheck(periodicity[i] == DM_BOUNDARY_NONE, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Periodicity not yet supported");
1530   PetscCall(DMCreate(PetscObjectComm((PetscObject)dm), &bdm));
1531   PetscCall(DMSetType(bdm, DMPLEX));
1532   PetscCall(DMSetDimension(bdm, 2));
1533   PetscCall(PetscLogEventBegin(DMPLEX_Generate, bdm, 0, 0, 0));
1534   PetscCall(DMPlexCreateBoxMesh_Simplex_Internal(bdm, 2, faces, lower, upper, periodicity, PETSC_TRUE));
1535   PetscCall(DMPlexExtrude(bdm, faces[2], upper[2] - lower[2], PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, NULL, NULL, &vol));
1536   PetscCall(PetscLogEventEnd(DMPLEX_Generate, bdm, 0, 0, 0));
1537   PetscCall(DMDestroy(&bdm));
1538   PetscCall(DMPlexReplace_Internal(dm, &vol));
1539   if (lower[2] != 0.0) {
1540     Vec          v;
1541     PetscScalar *x;
1542     PetscInt     cDim, n;
1543 
1544     PetscCall(DMGetCoordinatesLocal(dm, &v));
1545     PetscCall(VecGetBlockSize(v, &cDim));
1546     PetscCall(VecGetLocalSize(v, &n));
1547     PetscCall(VecGetArray(v, &x));
1548     x += cDim;
1549     for (i = 0; i < n; i += cDim) x[i] += lower[2];
1550     PetscCall(VecRestoreArray(v, &x));
1551     PetscCall(DMSetCoordinatesLocal(dm, v));
1552   }
1553   PetscFunctionReturn(PETSC_SUCCESS);
1554 }
1555 
1556 /*@
1557   DMPlexCreateWedgeBoxMesh - Creates a 3-D mesh tessellating the (x,y) plane and extruding in the third direction using wedge cells.
1558 
1559   Collective
1560 
1561   Input Parameters:
1562 + comm        - The communicator for the `DM` object
1563 . faces       - Number of faces per dimension, or `NULL` for (1, 1, 1)
1564 . lower       - The lower left corner, or `NULL` for (0, 0, 0)
1565 . upper       - The upper right corner, or `NULL` for (1, 1, 1)
1566 . periodicity - The boundary type for the X,Y,Z direction, or `NULL` for `DM_BOUNDARY_NONE`
1567 . orderHeight - If `PETSC_TRUE`, orders the extruded cells in the height first. Otherwise, orders the cell on the layers first
1568 - interpolate - Flag to create intermediate mesh pieces (edges, faces)
1569 
1570   Output Parameter:
1571 . dm - The `DM` object
1572 
1573   Level: beginner
1574 
1575 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateHexCylinderMesh()`, `DMPlexCreateWedgeCylinderMesh()`, `DMExtrude()`, `DMPlexCreateBoxMesh()`, `DMSetType()`, `DMCreate()`
1576 @*/
1577 PetscErrorCode DMPlexCreateWedgeBoxMesh(MPI_Comm comm, const PetscInt faces[], const PetscReal lower[], const PetscReal upper[], const DMBoundaryType periodicity[], PetscBool orderHeight, PetscBool interpolate, DM *dm)
1578 {
1579   PetscInt       fac[3] = {1, 1, 1};
1580   PetscReal      low[3] = {0, 0, 0};
1581   PetscReal      upp[3] = {1, 1, 1};
1582   DMBoundaryType bdt[3] = {DM_BOUNDARY_NONE, DM_BOUNDARY_NONE, DM_BOUNDARY_NONE};
1583 
1584   PetscFunctionBegin;
1585   PetscCall(DMCreate(comm, dm));
1586   PetscCall(DMSetType(*dm, DMPLEX));
1587   PetscCall(DMPlexCreateWedgeBoxMesh_Internal(*dm, faces ? faces : fac, lower ? lower : low, upper ? upper : upp, periodicity ? periodicity : bdt));
1588   if (!interpolate) {
1589     DM udm;
1590 
1591     PetscCall(DMPlexUninterpolate(*dm, &udm));
1592     PetscCall(DMPlexReplace_Internal(*dm, &udm));
1593   }
1594   if (periodicity) PetscCall(DMLocalizeCoordinates(*dm));
1595   PetscFunctionReturn(PETSC_SUCCESS);
1596 }
1597 
1598 /*
1599   DMPlexTensorPointLexicographic_Private - Returns all tuples of size 'len' with nonnegative integers that are all less than or equal to 'max' for that dimension.
1600 
1601   Input Parameters:
1602 + len - The length of the tuple
1603 . max - The maximum for each dimension, so values are in [0, max)
1604 - tup - A tuple of length len+1: tup[len] > 0 indicates a stopping condition
1605 
1606   Output Parameter:
1607 . tup - A tuple of `len` integers whose entries are at most `max`
1608 
1609   Level: developer
1610 
1611   Note:
1612   Ordering is lexicographic with lowest index as least significant in ordering.
1613   e.g. for len == 2 and max == 2, this will return, in order, {0,0}, {1,0}, {2,0}, {0,1}, {1,1}, {2,1}, {0,2}, {1,2}, {2,2}.
1614 
1615 .seealso: PetscDualSpaceTensorPointLexicographic_Internal(), PetscDualSpaceLatticePointLexicographic_Internal()
1616 */
1617 static PetscErrorCode DMPlexTensorPointLexicographic_Private(PetscInt len, const PetscInt max[], PetscInt tup[])
1618 {
1619   PetscInt i;
1620 
1621   PetscFunctionBegin;
1622   for (i = 0; i < len; ++i) {
1623     if (tup[i] < max[i] - 1) {
1624       break;
1625     } else {
1626       tup[i] = 0;
1627     }
1628   }
1629   if (i == len) tup[i - 1] = max[i - 1];
1630   else ++tup[i];
1631   PetscFunctionReturn(PETSC_SUCCESS);
1632 }
1633 
1634 static PetscInt TupleToIndex_Private(PetscInt len, const PetscInt max[], const PetscInt tup[])
1635 {
1636   PetscInt i, idx = tup[len - 1];
1637 
1638   for (i = len - 2; i >= 0; --i) {
1639     idx *= max[i];
1640     idx += tup[i];
1641   }
1642   return idx;
1643 }
1644 
1645 static PetscErrorCode DestroyExtent_Private(void *extent)
1646 {
1647   return PetscFree(extent);
1648 }
1649 
1650 static PetscErrorCode DMPlexCreateHypercubicMesh_Internal(DM dm, PetscInt dim, const PetscReal lower[], const PetscReal upper[], const PetscInt edges[], const DMBoundaryType bd[])
1651 {
1652   Vec          coordinates;
1653   PetscSection coordSection;
1654   DMLabel      cutLabel    = NULL;
1655   PetscBool    cutMarker   = PETSC_FALSE;
1656   PetscBool    periodic    = PETSC_FALSE;
1657   PetscInt     numCells    = 1, c;
1658   PetscInt     numVertices = 1, v;
1659   PetscScalar *coords;
1660   PetscInt    *vertices, *vert, *vtmp, *supp, cone[2];
1661   PetscInt     d, e, cell = 0, coordSize;
1662   PetscMPIInt  rank;
1663 
1664   PetscFunctionBegin;
1665   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1666   PetscCall(DMSetDimension(dm, dim));
1667   PetscCall(PetscCalloc4(dim, &vertices, dim, &vert, dim, &vtmp, 2 * dim, &supp));
1668   PetscCall(DMCreateLabel(dm, "marker"));
1669   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_periodic_cut", &cutMarker, NULL));
1670   for (d = 0; d < dim; ++d) periodic = (periodic || bd[d] == DM_BOUNDARY_PERIODIC) ? PETSC_TRUE : PETSC_FALSE;
1671   if (periodic && cutMarker) {
1672     PetscCall(DMCreateLabel(dm, "periodic_cut"));
1673     PetscCall(DMGetLabel(dm, "periodic_cut", &cutLabel));
1674   }
1675   for (d = 0; d < dim; ++d) PetscCheck(bd[d] == DM_BOUNDARY_PERIODIC, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Hypercubic mesh must be periodic now");
1676   for (d = 0; d < dim; ++d) {
1677     vertices[d] = edges[d];
1678     numVertices *= vertices[d];
1679   }
1680   numCells = numVertices * dim;
1681   PetscCall(DMPlexSetChart(dm, 0, numCells + numVertices));
1682   for (c = 0; c < numCells; ++c) PetscCall(DMPlexSetConeSize(dm, c, 2));
1683   for (v = numCells; v < numCells + numVertices; ++v) PetscCall(DMPlexSetSupportSize(dm, v, 2 * dim));
1684   /* TODO Loop over boundary and reset support sizes */
1685   PetscCall(DMSetUp(dm)); /* Allocate space for cones and supports */
1686   /* Build cell cones and vertex supports */
1687   PetscCall(DMCreateLabel(dm, "celltype"));
1688   while (vert[dim - 1] < vertices[dim - 1]) {
1689     const PetscInt vertex = TupleToIndex_Private(dim, vertices, vert) + numCells;
1690     PetscInt       s      = 0;
1691 
1692     PetscCall(PetscPrintf(PETSC_COMM_SELF, "Vertex %" PetscInt_FMT ":", vertex));
1693     for (d = 0; d < dim; ++d) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, vert[d]));
1694     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
1695     PetscCall(DMPlexSetCellType(dm, vertex, DM_POLYTOPE_POINT));
1696     for (d = 0; d < dim; ++d) {
1697       for (e = 0; e < dim; ++e) vtmp[e] = vert[e];
1698       vtmp[d] = (vert[d] + 1) % vertices[d];
1699       cone[0] = vertex;
1700       cone[1] = TupleToIndex_Private(dim, vertices, vtmp) + numCells;
1701       PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Vertex %" PetscInt_FMT ":", cone[1]));
1702       for (e = 0; e < dim; ++e) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, vtmp[e]));
1703       PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
1704       PetscCall(DMPlexSetCone(dm, cell, cone));
1705       PetscCall(DMPlexSetCellType(dm, cell, DM_POLYTOPE_SEGMENT));
1706       PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Edge %" PetscInt_FMT " (%" PetscInt_FMT " %" PetscInt_FMT ")\n", cell, cone[0], cone[1]));
1707       ++cell;
1708     }
1709     for (d = 0; d < dim; ++d) {
1710       for (e = 0; e < dim; ++e) vtmp[e] = vert[e];
1711       vtmp[d]   = (vert[d] + vertices[d] - 1) % vertices[d];
1712       supp[s++] = TupleToIndex_Private(dim, vertices, vtmp) * dim + d;
1713       supp[s++] = (vertex - numCells) * dim + d;
1714       PetscCall(DMPlexSetSupport(dm, vertex, supp));
1715     }
1716     PetscCall(DMPlexTensorPointLexicographic_Private(dim, vertices, vert));
1717   }
1718   PetscCall(DMPlexStratify(dm));
1719   /* Build coordinates */
1720   PetscCall(DMGetCoordinateSection(dm, &coordSection));
1721   PetscCall(PetscSectionSetNumFields(coordSection, 1));
1722   PetscCall(PetscSectionSetFieldComponents(coordSection, 0, dim));
1723   PetscCall(PetscSectionSetChart(coordSection, numCells, numCells + numVertices));
1724   for (v = numCells; v < numCells + numVertices; ++v) {
1725     PetscCall(PetscSectionSetDof(coordSection, v, dim));
1726     PetscCall(PetscSectionSetFieldDof(coordSection, v, 0, dim));
1727   }
1728   PetscCall(PetscSectionSetUp(coordSection));
1729   PetscCall(PetscSectionGetStorageSize(coordSection, &coordSize));
1730   PetscCall(VecCreate(PETSC_COMM_SELF, &coordinates));
1731   PetscCall(PetscObjectSetName((PetscObject)coordinates, "coordinates"));
1732   PetscCall(VecSetSizes(coordinates, coordSize, PETSC_DETERMINE));
1733   PetscCall(VecSetBlockSize(coordinates, dim));
1734   PetscCall(VecSetType(coordinates, VECSTANDARD));
1735   PetscCall(VecGetArray(coordinates, &coords));
1736   for (d = 0; d < dim; ++d) vert[d] = 0;
1737   while (vert[dim - 1] < vertices[dim - 1]) {
1738     const PetscInt vertex = TupleToIndex_Private(dim, vertices, vert);
1739 
1740     for (d = 0; d < dim; ++d) coords[vertex * dim + d] = lower[d] + ((upper[d] - lower[d]) / vertices[d]) * vert[d];
1741     PetscCall(PetscPrintf(PETSC_COMM_SELF, "Vertex %" PetscInt_FMT ":", vertex));
1742     for (d = 0; d < dim; ++d) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, vert[d]));
1743     for (d = 0; d < dim; ++d) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %g", (double)PetscRealPart(coords[vertex * dim + d])));
1744     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
1745     PetscCall(DMPlexTensorPointLexicographic_Private(dim, vertices, vert));
1746   }
1747   PetscCall(VecRestoreArray(coordinates, &coords));
1748   PetscCall(DMSetCoordinatesLocal(dm, coordinates));
1749   PetscCall(VecDestroy(&coordinates));
1750   PetscCall(PetscFree4(vertices, vert, vtmp, supp));
1751   //PetscCall(DMSetPeriodicity(dm, NULL, lower, upper));
1752   // Attach the extent
1753   {
1754     PetscContainer c;
1755     PetscInt      *extent;
1756 
1757     PetscCall(PetscMalloc1(dim, &extent));
1758     for (PetscInt d = 0; d < dim; ++d) extent[d] = edges[d];
1759     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &c));
1760     PetscCall(PetscContainerSetUserDestroy(c, DestroyExtent_Private));
1761     PetscCall(PetscContainerSetPointer(c, extent));
1762     PetscCall(PetscObjectCompose((PetscObject)dm, "_extent", (PetscObject)c));
1763     PetscCall(PetscContainerDestroy(&c));
1764   }
1765   PetscFunctionReturn(PETSC_SUCCESS);
1766 }
1767 
1768 /*@C
1769   DMPlexCreateHypercubicMesh - Creates a periodic mesh on the tensor product of unit intervals using only vertices and edges.
1770 
1771   Collective
1772 
1773   Input Parameters:
1774 + comm  - The communicator for the DM object
1775 . dim   - The spatial dimension
1776 . edges - Number of edges per dimension, or `NULL` for (1,) in 1D and (2, 2) in 2D and (1, 1, 1) in 3D
1777 . lower - The lower left corner, or `NULL` for (0, 0, 0)
1778 - upper - The upper right corner, or `NULL` for (1, 1, 1)
1779 
1780   Output Parameter:
1781 . dm - The DM object
1782 
1783   Level: beginner
1784 
1785   Note:
1786   If you want to customize this mesh using options, you just need to
1787 .vb
1788   DMCreate(comm, &dm);
1789   DMSetType(dm, DMPLEX);
1790   DMSetFromOptions(dm);
1791 .ve
1792   and use the options on the `DMSetFromOptions()` page.
1793 
1794   The vertices are numbered is lexicographic order, and the dim edges exiting a vertex in the positive orthant are number consecutively,
1795 .vb
1796  18--0-19--2-20--4-18
1797   |     |     |     |
1798  13    15    17    13
1799   |     |     |     |
1800  24-12-25-14-26-16-24
1801   |     |     |     |
1802   7     9    11     7
1803   |     |     |     |
1804  21--6-22--8-23-10-21
1805   |     |     |     |
1806   1     3     5     1
1807   |     |     |     |
1808  18--0-19--2-20--4-18
1809 .ve
1810 
1811 .seealso: `DMSetFromOptions()`, `DMPlexCreateFromFile()`, `DMPlexCreateHexCylinderMesh()`, `DMSetType()`, `DMCreate()`
1812 @*/
1813 PetscErrorCode DMPlexCreateHypercubicMesh(MPI_Comm comm, PetscInt dim, const PetscInt edges[], const PetscReal lower[], const PetscReal upper[], DM *dm)
1814 {
1815   PetscInt       *edg;
1816   PetscReal      *low, *upp;
1817   DMBoundaryType *bdt;
1818   PetscInt        d;
1819 
1820   PetscFunctionBegin;
1821   PetscCall(DMCreate(comm, dm));
1822   PetscCall(DMSetType(*dm, DMPLEX));
1823   PetscCall(PetscMalloc4(dim, &edg, dim, &low, dim, &upp, dim, &bdt));
1824   for (d = 0; d < dim; ++d) {
1825     edg[d] = edges ? edges[d] : 1;
1826     low[d] = lower ? lower[d] : 0.;
1827     upp[d] = upper ? upper[d] : 1.;
1828     bdt[d] = DM_BOUNDARY_PERIODIC;
1829   }
1830   PetscCall(DMPlexCreateHypercubicMesh_Internal(*dm, dim, low, upp, edg, bdt));
1831   PetscCall(PetscFree4(edg, low, upp, bdt));
1832   PetscFunctionReturn(PETSC_SUCCESS);
1833 }
1834 
1835 /*@
1836   DMPlexSetOptionsPrefix - Sets the prefix used for searching for all `DM` options in the database.
1837 
1838   Logically Collective
1839 
1840   Input Parameters:
1841 + dm     - the `DM` context
1842 - prefix - the prefix to prepend to all option names
1843 
1844   Level: advanced
1845 
1846   Note:
1847   A hyphen (-) must NOT be given at the beginning of the prefix name.
1848   The first character of all runtime options is AUTOMATICALLY the hyphen.
1849 
1850 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `SNESSetFromOptions()`
1851 @*/
1852 PetscErrorCode DMPlexSetOptionsPrefix(DM dm, const char prefix[])
1853 {
1854   DM_Plex *mesh = (DM_Plex *)dm->data;
1855 
1856   PetscFunctionBegin;
1857   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1858   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)dm, prefix));
1859   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)mesh->partitioner, prefix));
1860   PetscFunctionReturn(PETSC_SUCCESS);
1861 }
1862 
1863 /* Remap geometry to cylinder
1864    TODO: This only works for a single refinement, then it is broken
1865 
1866      Interior square: Linear interpolation is correct
1867      The other cells all have vertices on rays from the origin. We want to uniformly expand the spacing
1868      such that the last vertex is on the unit circle. So the closest and farthest vertices are at distance
1869 
1870        phi     = arctan(y/x)
1871        d_close = sqrt(1/8 + 1/4 sin^2(phi))
1872        d_far   = sqrt(1/2 + sin^2(phi))
1873 
1874      so we remap them using
1875 
1876        x_new = x_close + (x - x_close) (1 - d_close) / (d_far - d_close)
1877        y_new = y_close + (y - y_close) (1 - d_close) / (d_far - d_close)
1878 
1879      If pi/4 < phi < 3pi/4 or -3pi/4 < phi < -pi/4, then we switch x and y.
1880 */
1881 static void snapToCylinder(PetscInt dim, PetscInt Nf, PetscInt NfAux, const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[], const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[], PetscReal t, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar f0[])
1882 {
1883   const PetscReal dis = 1.0 / PetscSqrtReal(2.0);
1884   const PetscReal ds2 = 0.5 * dis;
1885 
1886   if ((PetscAbsScalar(u[0]) <= ds2) && (PetscAbsScalar(u[1]) <= ds2)) {
1887     f0[0] = u[0];
1888     f0[1] = u[1];
1889   } else {
1890     PetscReal phi, sinp, cosp, dc, df, x, y, xc, yc;
1891 
1892     x    = PetscRealPart(u[0]);
1893     y    = PetscRealPart(u[1]);
1894     phi  = PetscAtan2Real(y, x);
1895     sinp = PetscSinReal(phi);
1896     cosp = PetscCosReal(phi);
1897     if ((PetscAbsReal(phi) > PETSC_PI / 4.0) && (PetscAbsReal(phi) < 3.0 * PETSC_PI / 4.0)) {
1898       dc = PetscAbsReal(ds2 / sinp);
1899       df = PetscAbsReal(dis / sinp);
1900       xc = ds2 * x / PetscAbsReal(y);
1901       yc = ds2 * PetscSignReal(y);
1902     } else {
1903       dc = PetscAbsReal(ds2 / cosp);
1904       df = PetscAbsReal(dis / cosp);
1905       xc = ds2 * PetscSignReal(x);
1906       yc = ds2 * y / PetscAbsReal(x);
1907     }
1908     f0[0] = xc + (u[0] - xc) * (1.0 - dc) / (df - dc);
1909     f0[1] = yc + (u[1] - yc) * (1.0 - dc) / (df - dc);
1910   }
1911   f0[2] = u[2];
1912 }
1913 
1914 static PetscErrorCode DMPlexCreateHexCylinderMesh_Internal(DM dm, DMBoundaryType periodicZ, PetscInt Nr)
1915 {
1916   const PetscInt dim = 3;
1917   PetscInt       numCells, numVertices;
1918   PetscMPIInt    rank;
1919 
1920   PetscFunctionBegin;
1921   PetscCall(PetscLogEventBegin(DMPLEX_Generate, dm, 0, 0, 0));
1922   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1923   PetscCall(DMSetDimension(dm, dim));
1924   /* Create topology */
1925   {
1926     PetscInt cone[8], c;
1927 
1928     numCells    = rank == 0 ? 5 : 0;
1929     numVertices = rank == 0 ? 16 : 0;
1930     if (periodicZ == DM_BOUNDARY_PERIODIC) {
1931       numCells *= 3;
1932       numVertices = rank == 0 ? 24 : 0;
1933     }
1934     PetscCall(DMPlexSetChart(dm, 0, numCells + numVertices));
1935     for (c = 0; c < numCells; c++) PetscCall(DMPlexSetConeSize(dm, c, 8));
1936     PetscCall(DMSetUp(dm));
1937     if (rank == 0) {
1938       if (periodicZ == DM_BOUNDARY_PERIODIC) {
1939         cone[0] = 15;
1940         cone[1] = 18;
1941         cone[2] = 17;
1942         cone[3] = 16;
1943         cone[4] = 31;
1944         cone[5] = 32;
1945         cone[6] = 33;
1946         cone[7] = 34;
1947         PetscCall(DMPlexSetCone(dm, 0, cone));
1948         cone[0] = 16;
1949         cone[1] = 17;
1950         cone[2] = 24;
1951         cone[3] = 23;
1952         cone[4] = 32;
1953         cone[5] = 36;
1954         cone[6] = 37;
1955         cone[7] = 33; /* 22 25 26 21 */
1956         PetscCall(DMPlexSetCone(dm, 1, cone));
1957         cone[0] = 18;
1958         cone[1] = 27;
1959         cone[2] = 24;
1960         cone[3] = 17;
1961         cone[4] = 34;
1962         cone[5] = 33;
1963         cone[6] = 37;
1964         cone[7] = 38;
1965         PetscCall(DMPlexSetCone(dm, 2, cone));
1966         cone[0] = 29;
1967         cone[1] = 27;
1968         cone[2] = 18;
1969         cone[3] = 15;
1970         cone[4] = 35;
1971         cone[5] = 31;
1972         cone[6] = 34;
1973         cone[7] = 38;
1974         PetscCall(DMPlexSetCone(dm, 3, cone));
1975         cone[0] = 29;
1976         cone[1] = 15;
1977         cone[2] = 16;
1978         cone[3] = 23;
1979         cone[4] = 35;
1980         cone[5] = 36;
1981         cone[6] = 32;
1982         cone[7] = 31;
1983         PetscCall(DMPlexSetCone(dm, 4, cone));
1984 
1985         cone[0] = 31;
1986         cone[1] = 34;
1987         cone[2] = 33;
1988         cone[3] = 32;
1989         cone[4] = 19;
1990         cone[5] = 22;
1991         cone[6] = 21;
1992         cone[7] = 20;
1993         PetscCall(DMPlexSetCone(dm, 5, cone));
1994         cone[0] = 32;
1995         cone[1] = 33;
1996         cone[2] = 37;
1997         cone[3] = 36;
1998         cone[4] = 22;
1999         cone[5] = 25;
2000         cone[6] = 26;
2001         cone[7] = 21;
2002         PetscCall(DMPlexSetCone(dm, 6, cone));
2003         cone[0] = 34;
2004         cone[1] = 38;
2005         cone[2] = 37;
2006         cone[3] = 33;
2007         cone[4] = 20;
2008         cone[5] = 21;
2009         cone[6] = 26;
2010         cone[7] = 28;
2011         PetscCall(DMPlexSetCone(dm, 7, cone));
2012         cone[0] = 35;
2013         cone[1] = 38;
2014         cone[2] = 34;
2015         cone[3] = 31;
2016         cone[4] = 30;
2017         cone[5] = 19;
2018         cone[6] = 20;
2019         cone[7] = 28;
2020         PetscCall(DMPlexSetCone(dm, 8, cone));
2021         cone[0] = 35;
2022         cone[1] = 31;
2023         cone[2] = 32;
2024         cone[3] = 36;
2025         cone[4] = 30;
2026         cone[5] = 25;
2027         cone[6] = 22;
2028         cone[7] = 19;
2029         PetscCall(DMPlexSetCone(dm, 9, cone));
2030 
2031         cone[0] = 19;
2032         cone[1] = 20;
2033         cone[2] = 21;
2034         cone[3] = 22;
2035         cone[4] = 15;
2036         cone[5] = 16;
2037         cone[6] = 17;
2038         cone[7] = 18;
2039         PetscCall(DMPlexSetCone(dm, 10, cone));
2040         cone[0] = 22;
2041         cone[1] = 21;
2042         cone[2] = 26;
2043         cone[3] = 25;
2044         cone[4] = 16;
2045         cone[5] = 23;
2046         cone[6] = 24;
2047         cone[7] = 17;
2048         PetscCall(DMPlexSetCone(dm, 11, cone));
2049         cone[0] = 20;
2050         cone[1] = 28;
2051         cone[2] = 26;
2052         cone[3] = 21;
2053         cone[4] = 18;
2054         cone[5] = 17;
2055         cone[6] = 24;
2056         cone[7] = 27;
2057         PetscCall(DMPlexSetCone(dm, 12, cone));
2058         cone[0] = 30;
2059         cone[1] = 28;
2060         cone[2] = 20;
2061         cone[3] = 19;
2062         cone[4] = 29;
2063         cone[5] = 15;
2064         cone[6] = 18;
2065         cone[7] = 27;
2066         PetscCall(DMPlexSetCone(dm, 13, cone));
2067         cone[0] = 30;
2068         cone[1] = 19;
2069         cone[2] = 22;
2070         cone[3] = 25;
2071         cone[4] = 29;
2072         cone[5] = 23;
2073         cone[6] = 16;
2074         cone[7] = 15;
2075         PetscCall(DMPlexSetCone(dm, 14, cone));
2076       } else {
2077         cone[0] = 5;
2078         cone[1] = 8;
2079         cone[2] = 7;
2080         cone[3] = 6;
2081         cone[4] = 9;
2082         cone[5] = 12;
2083         cone[6] = 11;
2084         cone[7] = 10;
2085         PetscCall(DMPlexSetCone(dm, 0, cone));
2086         cone[0] = 6;
2087         cone[1] = 7;
2088         cone[2] = 14;
2089         cone[3] = 13;
2090         cone[4] = 12;
2091         cone[5] = 15;
2092         cone[6] = 16;
2093         cone[7] = 11;
2094         PetscCall(DMPlexSetCone(dm, 1, cone));
2095         cone[0] = 8;
2096         cone[1] = 17;
2097         cone[2] = 14;
2098         cone[3] = 7;
2099         cone[4] = 10;
2100         cone[5] = 11;
2101         cone[6] = 16;
2102         cone[7] = 18;
2103         PetscCall(DMPlexSetCone(dm, 2, cone));
2104         cone[0] = 19;
2105         cone[1] = 17;
2106         cone[2] = 8;
2107         cone[3] = 5;
2108         cone[4] = 20;
2109         cone[5] = 9;
2110         cone[6] = 10;
2111         cone[7] = 18;
2112         PetscCall(DMPlexSetCone(dm, 3, cone));
2113         cone[0] = 19;
2114         cone[1] = 5;
2115         cone[2] = 6;
2116         cone[3] = 13;
2117         cone[4] = 20;
2118         cone[5] = 15;
2119         cone[6] = 12;
2120         cone[7] = 9;
2121         PetscCall(DMPlexSetCone(dm, 4, cone));
2122       }
2123     }
2124     PetscCall(DMPlexSymmetrize(dm));
2125     PetscCall(DMPlexStratify(dm));
2126   }
2127   /* Create cube geometry */
2128   {
2129     Vec             coordinates;
2130     PetscSection    coordSection;
2131     PetscScalar    *coords;
2132     PetscInt        coordSize, v;
2133     const PetscReal dis = 1.0 / PetscSqrtReal(2.0);
2134     const PetscReal ds2 = dis / 2.0;
2135 
2136     /* Build coordinates */
2137     PetscCall(DMGetCoordinateSection(dm, &coordSection));
2138     PetscCall(PetscSectionSetNumFields(coordSection, 1));
2139     PetscCall(PetscSectionSetFieldComponents(coordSection, 0, dim));
2140     PetscCall(PetscSectionSetChart(coordSection, numCells, numCells + numVertices));
2141     for (v = numCells; v < numCells + numVertices; ++v) {
2142       PetscCall(PetscSectionSetDof(coordSection, v, dim));
2143       PetscCall(PetscSectionSetFieldDof(coordSection, v, 0, dim));
2144     }
2145     PetscCall(PetscSectionSetUp(coordSection));
2146     PetscCall(PetscSectionGetStorageSize(coordSection, &coordSize));
2147     PetscCall(VecCreate(PETSC_COMM_SELF, &coordinates));
2148     PetscCall(PetscObjectSetName((PetscObject)coordinates, "coordinates"));
2149     PetscCall(VecSetSizes(coordinates, coordSize, PETSC_DETERMINE));
2150     PetscCall(VecSetBlockSize(coordinates, dim));
2151     PetscCall(VecSetType(coordinates, VECSTANDARD));
2152     PetscCall(VecGetArray(coordinates, &coords));
2153     if (rank == 0) {
2154       coords[0 * dim + 0]  = -ds2;
2155       coords[0 * dim + 1]  = -ds2;
2156       coords[0 * dim + 2]  = 0.0;
2157       coords[1 * dim + 0]  = ds2;
2158       coords[1 * dim + 1]  = -ds2;
2159       coords[1 * dim + 2]  = 0.0;
2160       coords[2 * dim + 0]  = ds2;
2161       coords[2 * dim + 1]  = ds2;
2162       coords[2 * dim + 2]  = 0.0;
2163       coords[3 * dim + 0]  = -ds2;
2164       coords[3 * dim + 1]  = ds2;
2165       coords[3 * dim + 2]  = 0.0;
2166       coords[4 * dim + 0]  = -ds2;
2167       coords[4 * dim + 1]  = -ds2;
2168       coords[4 * dim + 2]  = 1.0;
2169       coords[5 * dim + 0]  = -ds2;
2170       coords[5 * dim + 1]  = ds2;
2171       coords[5 * dim + 2]  = 1.0;
2172       coords[6 * dim + 0]  = ds2;
2173       coords[6 * dim + 1]  = ds2;
2174       coords[6 * dim + 2]  = 1.0;
2175       coords[7 * dim + 0]  = ds2;
2176       coords[7 * dim + 1]  = -ds2;
2177       coords[7 * dim + 2]  = 1.0;
2178       coords[8 * dim + 0]  = dis;
2179       coords[8 * dim + 1]  = -dis;
2180       coords[8 * dim + 2]  = 0.0;
2181       coords[9 * dim + 0]  = dis;
2182       coords[9 * dim + 1]  = dis;
2183       coords[9 * dim + 2]  = 0.0;
2184       coords[10 * dim + 0] = dis;
2185       coords[10 * dim + 1] = -dis;
2186       coords[10 * dim + 2] = 1.0;
2187       coords[11 * dim + 0] = dis;
2188       coords[11 * dim + 1] = dis;
2189       coords[11 * dim + 2] = 1.0;
2190       coords[12 * dim + 0] = -dis;
2191       coords[12 * dim + 1] = dis;
2192       coords[12 * dim + 2] = 0.0;
2193       coords[13 * dim + 0] = -dis;
2194       coords[13 * dim + 1] = dis;
2195       coords[13 * dim + 2] = 1.0;
2196       coords[14 * dim + 0] = -dis;
2197       coords[14 * dim + 1] = -dis;
2198       coords[14 * dim + 2] = 0.0;
2199       coords[15 * dim + 0] = -dis;
2200       coords[15 * dim + 1] = -dis;
2201       coords[15 * dim + 2] = 1.0;
2202       if (periodicZ == DM_BOUNDARY_PERIODIC) {
2203         /* 15 31 19 */ coords[16 * dim + 0] = -ds2;
2204         coords[16 * dim + 1]                = -ds2;
2205         coords[16 * dim + 2]                = 0.5;
2206         /* 16 32 22 */ coords[17 * dim + 0] = ds2;
2207         coords[17 * dim + 1]                = -ds2;
2208         coords[17 * dim + 2]                = 0.5;
2209         /* 17 33 21 */ coords[18 * dim + 0] = ds2;
2210         coords[18 * dim + 1]                = ds2;
2211         coords[18 * dim + 2]                = 0.5;
2212         /* 18 34 20 */ coords[19 * dim + 0] = -ds2;
2213         coords[19 * dim + 1]                = ds2;
2214         coords[19 * dim + 2]                = 0.5;
2215         /* 29 35 30 */ coords[20 * dim + 0] = -dis;
2216         coords[20 * dim + 1]                = -dis;
2217         coords[20 * dim + 2]                = 0.5;
2218         /* 23 36 25 */ coords[21 * dim + 0] = dis;
2219         coords[21 * dim + 1]                = -dis;
2220         coords[21 * dim + 2]                = 0.5;
2221         /* 24 37 26 */ coords[22 * dim + 0] = dis;
2222         coords[22 * dim + 1]                = dis;
2223         coords[22 * dim + 2]                = 0.5;
2224         /* 27 38 28 */ coords[23 * dim + 0] = -dis;
2225         coords[23 * dim + 1]                = dis;
2226         coords[23 * dim + 2]                = 0.5;
2227       }
2228     }
2229     PetscCall(VecRestoreArray(coordinates, &coords));
2230     PetscCall(DMSetCoordinatesLocal(dm, coordinates));
2231     PetscCall(VecDestroy(&coordinates));
2232   }
2233   /* Create periodicity */
2234   if (periodicZ == DM_BOUNDARY_PERIODIC || periodicZ == DM_BOUNDARY_TWIST) {
2235     PetscReal L[3]       = {-1., -1., 0.};
2236     PetscReal maxCell[3] = {-1., -1., 0.};
2237     PetscReal lower[3]   = {0.0, 0.0, 0.0};
2238     PetscReal upper[3]   = {1.0, 1.0, 1.5};
2239     PetscInt  numZCells  = 3;
2240 
2241     L[2]       = upper[2] - lower[2];
2242     maxCell[2] = 1.1 * (L[2] / numZCells);
2243     PetscCall(DMSetPeriodicity(dm, maxCell, lower, L));
2244   }
2245   {
2246     DM          cdm;
2247     PetscDS     cds;
2248     PetscScalar c[2] = {1.0, 1.0};
2249 
2250     PetscCall(DMPlexCreateCoordinateSpace(dm, 1, PETSC_TRUE, NULL));
2251     PetscCall(DMGetCoordinateDM(dm, &cdm));
2252     PetscCall(DMGetDS(cdm, &cds));
2253     PetscCall(PetscDSSetConstants(cds, 2, c));
2254   }
2255   PetscCall(PetscLogEventEnd(DMPLEX_Generate, dm, 0, 0, 0));
2256 
2257   /* Wait for coordinate creation before doing in-place modification */
2258   PetscCall(DMPlexInterpolateInPlace_Internal(dm));
2259 
2260   char        oldprefix[PETSC_MAX_PATH_LEN];
2261   const char *prefix;
2262 
2263   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
2264   PetscCall(PetscStrncpy(oldprefix, prefix, PETSC_MAX_PATH_LEN));
2265   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)dm, "petsc_cyl_ref_"));
2266   for (PetscInt r = 0; r < PetscMax(0, Nr); ++r) {
2267     DM rdm;
2268 
2269     PetscCall(DMRefine(dm, PetscObjectComm((PetscObject)dm), &rdm));
2270     PetscCall(DMPlexReplace_Internal(dm, &rdm));
2271   }
2272   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)dm, oldprefix));
2273   PetscCall(DMPlexRemapGeometry(dm, 0.0, snapToCylinder));
2274 
2275   DMLabel         bdlabel, edgelabel;
2276   IS              faceIS;
2277   const PetscInt *faces;
2278   PetscInt        Nf;
2279 
2280   PetscCall(DMCreateLabel(dm, "marker"));
2281   PetscCall(DMGetLabel(dm, "marker", &bdlabel));
2282   PetscCall(DMCreateLabel(dm, "generatrix"));
2283   PetscCall(DMGetLabel(dm, "generatrix", &edgelabel));
2284   PetscCall(DMPlexMarkBoundaryFaces(dm, PETSC_DETERMINE, bdlabel));
2285   // Remove faces on top and bottom
2286   PetscCall(DMLabelGetStratumIS(bdlabel, 1, &faceIS));
2287   if (faceIS) {
2288     PetscCall(ISGetLocalSize(faceIS, &Nf));
2289     PetscCall(ISGetIndices(faceIS, &faces));
2290     for (PetscInt f = 0; f < Nf; ++f) {
2291       PetscReal vol, normal[3];
2292 
2293       PetscCall(DMPlexComputeCellGeometryFVM(dm, faces[f], &vol, NULL, normal));
2294       if (PetscAbsReal(normal[2]) < PETSC_SMALL) PetscCall(DMLabelSetValue(edgelabel, faces[f], 1));
2295     }
2296     PetscCall(ISRestoreIndices(faceIS, &faces));
2297     PetscCall(ISDestroy(&faceIS));
2298   }
2299   PetscCall(DMPlexLabelComplete(dm, bdlabel));
2300   PetscCall(DMPlexLabelComplete(dm, edgelabel));
2301   PetscFunctionReturn(PETSC_SUCCESS);
2302 }
2303 
2304 /*@
2305   DMPlexCreateHexCylinderMesh - Creates a mesh on the tensor product of the unit interval with the circle (cylinder) using hexahedra.
2306 
2307   Collective
2308 
2309   Input Parameters:
2310 + comm      - The communicator for the `DM` object
2311 . periodicZ - The boundary type for the Z direction
2312 - Nr        - The number of refinements to carry out
2313 
2314   Output Parameter:
2315 . dm - The `DM` object
2316 
2317   Level: beginner
2318 
2319   Note:
2320   Here is the output numbering looking from the bottom of the cylinder\:
2321 .vb
2322        17-----14
2323         |     |
2324         |  2  |
2325         |     |
2326  17-----8-----7-----14
2327   |     |     |     |
2328   |  3  |  0  |  1  |
2329   |     |     |     |
2330  19-----5-----6-----13
2331         |     |
2332         |  4  |
2333         |     |
2334        19-----13
2335 
2336  and up through the top
2337 
2338        18-----16
2339         |     |
2340         |  2  |
2341         |     |
2342  18----10----11-----16
2343   |     |     |     |
2344   |  3  |  0  |  1  |
2345   |     |     |     |
2346  20-----9----12-----15
2347         |     |
2348         |  4  |
2349         |     |
2350        20-----15
2351 .ve
2352 
2353 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateBoxMesh()`, `DMSetType()`, `DMCreate()`
2354 @*/
2355 PetscErrorCode DMPlexCreateHexCylinderMesh(MPI_Comm comm, DMBoundaryType periodicZ, PetscInt Nr, DM *dm)
2356 {
2357   PetscFunctionBegin;
2358   PetscAssertPointer(dm, 4);
2359   PetscCall(DMCreate(comm, dm));
2360   PetscCall(DMSetType(*dm, DMPLEX));
2361   PetscCall(DMPlexCreateHexCylinderMesh_Internal(*dm, periodicZ, Nr));
2362   PetscFunctionReturn(PETSC_SUCCESS);
2363 }
2364 
2365 static PetscErrorCode DMPlexCreateWedgeCylinderMesh_Internal(DM dm, PetscInt n, PetscBool interpolate)
2366 {
2367   const PetscInt dim = 3;
2368   PetscInt       numCells, numVertices, v;
2369   PetscMPIInt    rank;
2370 
2371   PetscFunctionBegin;
2372   PetscCheck(n >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of wedges %" PetscInt_FMT " cannot be negative", n);
2373   PetscCall(PetscLogEventBegin(DMPLEX_Generate, dm, 0, 0, 0));
2374   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
2375   PetscCall(DMSetDimension(dm, dim));
2376   /* Must create the celltype label here so that we do not automatically try to compute the types */
2377   PetscCall(DMCreateLabel(dm, "celltype"));
2378   /* Create topology */
2379   {
2380     PetscInt cone[6], c;
2381 
2382     numCells    = rank == 0 ? n : 0;
2383     numVertices = rank == 0 ? 2 * (n + 1) : 0;
2384     PetscCall(DMPlexSetChart(dm, 0, numCells + numVertices));
2385     for (c = 0; c < numCells; c++) PetscCall(DMPlexSetConeSize(dm, c, 6));
2386     PetscCall(DMSetUp(dm));
2387     for (c = 0; c < numCells; c++) {
2388       cone[0] = c + n * 1;
2389       cone[1] = (c + 1) % n + n * 1;
2390       cone[2] = 0 + 3 * n;
2391       cone[3] = c + n * 2;
2392       cone[4] = (c + 1) % n + n * 2;
2393       cone[5] = 1 + 3 * n;
2394       PetscCall(DMPlexSetCone(dm, c, cone));
2395       PetscCall(DMPlexSetCellType(dm, c, DM_POLYTOPE_TRI_PRISM_TENSOR));
2396     }
2397     PetscCall(DMPlexSymmetrize(dm));
2398     PetscCall(DMPlexStratify(dm));
2399   }
2400   for (v = numCells; v < numCells + numVertices; ++v) PetscCall(DMPlexSetCellType(dm, v, DM_POLYTOPE_POINT));
2401   /* Create cylinder geometry */
2402   {
2403     Vec          coordinates;
2404     PetscSection coordSection;
2405     PetscScalar *coords;
2406     PetscInt     coordSize, c;
2407 
2408     /* Build coordinates */
2409     PetscCall(DMGetCoordinateSection(dm, &coordSection));
2410     PetscCall(PetscSectionSetNumFields(coordSection, 1));
2411     PetscCall(PetscSectionSetFieldComponents(coordSection, 0, dim));
2412     PetscCall(PetscSectionSetChart(coordSection, numCells, numCells + numVertices));
2413     for (v = numCells; v < numCells + numVertices; ++v) {
2414       PetscCall(PetscSectionSetDof(coordSection, v, dim));
2415       PetscCall(PetscSectionSetFieldDof(coordSection, v, 0, dim));
2416     }
2417     PetscCall(PetscSectionSetUp(coordSection));
2418     PetscCall(PetscSectionGetStorageSize(coordSection, &coordSize));
2419     PetscCall(VecCreate(PETSC_COMM_SELF, &coordinates));
2420     PetscCall(PetscObjectSetName((PetscObject)coordinates, "coordinates"));
2421     PetscCall(VecSetSizes(coordinates, coordSize, PETSC_DETERMINE));
2422     PetscCall(VecSetBlockSize(coordinates, dim));
2423     PetscCall(VecSetType(coordinates, VECSTANDARD));
2424     PetscCall(VecGetArray(coordinates, &coords));
2425     for (c = 0; c < numCells; c++) {
2426       coords[(c + 0 * n) * dim + 0] = PetscCosReal(2.0 * c * PETSC_PI / n);
2427       coords[(c + 0 * n) * dim + 1] = PetscSinReal(2.0 * c * PETSC_PI / n);
2428       coords[(c + 0 * n) * dim + 2] = 1.0;
2429       coords[(c + 1 * n) * dim + 0] = PetscCosReal(2.0 * c * PETSC_PI / n);
2430       coords[(c + 1 * n) * dim + 1] = PetscSinReal(2.0 * c * PETSC_PI / n);
2431       coords[(c + 1 * n) * dim + 2] = 0.0;
2432     }
2433     if (rank == 0) {
2434       coords[(2 * n + 0) * dim + 0] = 0.0;
2435       coords[(2 * n + 0) * dim + 1] = 0.0;
2436       coords[(2 * n + 0) * dim + 2] = 1.0;
2437       coords[(2 * n + 1) * dim + 0] = 0.0;
2438       coords[(2 * n + 1) * dim + 1] = 0.0;
2439       coords[(2 * n + 1) * dim + 2] = 0.0;
2440     }
2441     PetscCall(VecRestoreArray(coordinates, &coords));
2442     PetscCall(DMSetCoordinatesLocal(dm, coordinates));
2443     PetscCall(VecDestroy(&coordinates));
2444   }
2445   PetscCall(PetscLogEventEnd(DMPLEX_Generate, dm, 0, 0, 0));
2446   /* Interpolate */
2447   if (interpolate) PetscCall(DMPlexInterpolateInPlace_Internal(dm));
2448   PetscFunctionReturn(PETSC_SUCCESS);
2449 }
2450 
2451 /*@
2452   DMPlexCreateWedgeCylinderMesh - Creates a mesh on the tensor product of the unit interval with the circle (cylinder) using wedges.
2453 
2454   Collective
2455 
2456   Input Parameters:
2457 + comm        - The communicator for the `DM` object
2458 . n           - The number of wedges around the origin
2459 - interpolate - Create edges and faces
2460 
2461   Output Parameter:
2462 . dm - The `DM` object
2463 
2464   Level: beginner
2465 
2466 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateHexCylinderMesh()`, `DMPlexCreateBoxMesh()`, `DMSetType()`, `DMCreate()`
2467 @*/
2468 PetscErrorCode DMPlexCreateWedgeCylinderMesh(MPI_Comm comm, PetscInt n, PetscBool interpolate, DM *dm)
2469 {
2470   PetscFunctionBegin;
2471   PetscAssertPointer(dm, 4);
2472   PetscCall(DMCreate(comm, dm));
2473   PetscCall(DMSetType(*dm, DMPLEX));
2474   PetscCall(DMPlexCreateWedgeCylinderMesh_Internal(*dm, n, interpolate));
2475   PetscFunctionReturn(PETSC_SUCCESS);
2476 }
2477 
2478 static inline PetscReal DiffNormReal(PetscInt dim, const PetscReal x[], const PetscReal y[])
2479 {
2480   PetscReal prod = 0.0;
2481   PetscInt  i;
2482   for (i = 0; i < dim; ++i) prod += PetscSqr(x[i] - y[i]);
2483   return PetscSqrtReal(prod);
2484 }
2485 
2486 static inline PetscReal DotReal(PetscInt dim, const PetscReal x[], const PetscReal y[])
2487 {
2488   PetscReal prod = 0.0;
2489   PetscInt  i;
2490   for (i = 0; i < dim; ++i) prod += x[i] * y[i];
2491   return prod;
2492 }
2493 
2494 /* The first constant is the sphere radius */
2495 static void snapToSphere(PetscInt dim, PetscInt Nf, PetscInt NfAux, const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[], const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[], PetscReal t, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar f0[])
2496 {
2497   PetscReal r     = PetscRealPart(constants[0]);
2498   PetscReal norm2 = 0.0, fac;
2499   PetscInt  n     = uOff[1] - uOff[0], d;
2500 
2501   for (d = 0; d < n; ++d) norm2 += PetscSqr(PetscRealPart(u[d]));
2502   fac = r / PetscSqrtReal(norm2);
2503   for (d = 0; d < n; ++d) f0[d] = u[d] * fac;
2504 }
2505 
2506 static PetscErrorCode DMPlexCreateSphereMesh_Internal(DM dm, PetscInt dim, PetscBool simplex, PetscReal R)
2507 {
2508   const PetscInt embedDim = dim + 1;
2509   PetscSection   coordSection;
2510   Vec            coordinates;
2511   PetscScalar   *coords;
2512   PetscReal     *coordsIn;
2513   PetscInt       numCells, numEdges, numVerts = 0, firstVertex = 0, v, firstEdge, coordSize, d, e;
2514   PetscMPIInt    rank;
2515 
2516   PetscFunctionBegin;
2517   PetscValidLogicalCollectiveBool(dm, simplex, 3);
2518   PetscCall(PetscLogEventBegin(DMPLEX_Generate, dm, 0, 0, 0));
2519   PetscCall(DMSetDimension(dm, dim));
2520   PetscCall(DMSetCoordinateDim(dm, dim + 1));
2521   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
2522   switch (dim) {
2523   case 1:
2524     numCells = 16;
2525     numVerts = numCells;
2526 
2527     // Build Topology
2528     PetscCall(DMPlexSetChart(dm, 0, numCells + numVerts));
2529     for (PetscInt c = 0; c < numCells; c++) PetscCall(DMPlexSetConeSize(dm, c, embedDim));
2530     PetscCall(DMSetUp(dm));
2531     for (PetscInt c = 0; c < numCells; ++c) {
2532       PetscInt cone[2];
2533 
2534       cone[0] = c + numCells;
2535       cone[1] = (c + 1) % numVerts + numCells;
2536       PetscCall(DMPlexSetCone(dm, c, cone));
2537     }
2538     PetscCall(DMPlexSymmetrize(dm));
2539     PetscCall(DMPlexStratify(dm));
2540     PetscCall(PetscMalloc1(numVerts * embedDim, &coordsIn));
2541     for (PetscInt v = 0; v < numVerts; ++v) {
2542       const PetscReal rad = 2. * PETSC_PI * v / numVerts;
2543 
2544       coordsIn[v * embedDim + 0] = PetscCosReal(rad);
2545       coordsIn[v * embedDim + 1] = PetscSinReal(rad);
2546     }
2547     break;
2548   case 2:
2549     if (simplex) {
2550       const PetscReal radius    = PetscSqrtReal(1 + PETSC_PHI * PETSC_PHI) / (1.0 + PETSC_PHI);
2551       const PetscReal edgeLen   = 2.0 / (1.0 + PETSC_PHI) * (R / radius);
2552       const PetscInt  degree    = 5;
2553       PetscReal       vertex[3] = {0.0, 1.0 / (1.0 + PETSC_PHI), PETSC_PHI / (1.0 + PETSC_PHI)};
2554       PetscInt        s[3]      = {1, 1, 1};
2555       PetscInt        cone[3];
2556       PetscInt       *graph;
2557 
2558       vertex[0] *= R / radius;
2559       vertex[1] *= R / radius;
2560       vertex[2] *= R / radius;
2561       numCells    = rank == 0 ? 20 : 0;
2562       numVerts    = rank == 0 ? 12 : 0;
2563       firstVertex = numCells;
2564       /* Use icosahedron, which for a R-sphere has coordinates which are all cyclic permutations of
2565 
2566            (0, \pm 1/\phi+1, \pm \phi/\phi+1)
2567 
2568          where \phi^2 - \phi - 1 = 0, meaning \phi is the golden ratio \frac{1 + \sqrt{5}}{2}. The edge
2569          length is then given by 2/(1+\phi) = 2 * 0.38197 = 0.76393.
2570       */
2571       /* Construct vertices */
2572       PetscCall(PetscCalloc1(numVerts * embedDim, &coordsIn));
2573       if (rank == 0) {
2574         for (PetscInt p = 0, i = 0; p < embedDim; ++p) {
2575           for (s[1] = -1; s[1] < 2; s[1] += 2) {
2576             for (s[2] = -1; s[2] < 2; s[2] += 2) {
2577               for (d = 0; d < embedDim; ++d) coordsIn[i * embedDim + d] = s[(d + p) % embedDim] * vertex[(d + p) % embedDim];
2578               ++i;
2579             }
2580           }
2581         }
2582       }
2583       /* Construct graph */
2584       PetscCall(PetscCalloc1(numVerts * numVerts, &graph));
2585       for (PetscInt i = 0; i < numVerts; ++i) {
2586         PetscInt k = 0;
2587         for (PetscInt j = 0; j < numVerts; ++j) {
2588           if (PetscAbsReal(DiffNormReal(embedDim, &coordsIn[i * embedDim], &coordsIn[j * embedDim]) - edgeLen) < PETSC_SMALL) {
2589             graph[i * numVerts + j] = 1;
2590             ++k;
2591           }
2592         }
2593         PetscCheck(k == degree, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid icosahedron, vertex %" PetscInt_FMT " degree %" PetscInt_FMT " != %" PetscInt_FMT, i, k, degree);
2594       }
2595       /* Build Topology */
2596       PetscCall(DMPlexSetChart(dm, 0, numCells + numVerts));
2597       for (PetscInt c = 0; c < numCells; c++) PetscCall(DMPlexSetConeSize(dm, c, embedDim));
2598       PetscCall(DMSetUp(dm)); /* Allocate space for cones */
2599       /* Cells */
2600       for (PetscInt i = 0, c = 0; i < numVerts; ++i) {
2601         for (PetscInt j = 0; j < i; ++j) {
2602           for (PetscInt k = 0; k < j; ++k) {
2603             if (graph[i * numVerts + j] && graph[j * numVerts + k] && graph[k * numVerts + i]) {
2604               cone[0] = firstVertex + i;
2605               cone[1] = firstVertex + j;
2606               cone[2] = firstVertex + k;
2607               /* Check orientation */
2608               {
2609                 const PetscInt epsilon[3][3][3] = {
2610                   {{0, 0, 0},  {0, 0, 1},  {0, -1, 0}},
2611                   {{0, 0, -1}, {0, 0, 0},  {1, 0, 0} },
2612                   {{0, 1, 0},  {-1, 0, 0}, {0, 0, 0} }
2613                 };
2614                 PetscReal normal[3];
2615                 PetscInt  e, f;
2616 
2617                 for (d = 0; d < embedDim; ++d) {
2618                   normal[d] = 0.0;
2619                   for (e = 0; e < embedDim; ++e) {
2620                     for (f = 0; f < embedDim; ++f) normal[d] += epsilon[d][e][f] * (coordsIn[j * embedDim + e] - coordsIn[i * embedDim + e]) * (coordsIn[k * embedDim + f] - coordsIn[i * embedDim + f]);
2621                   }
2622                 }
2623                 if (DotReal(embedDim, normal, &coordsIn[i * embedDim]) < 0) {
2624                   PetscInt tmp = cone[1];
2625                   cone[1]      = cone[2];
2626                   cone[2]      = tmp;
2627                 }
2628               }
2629               PetscCall(DMPlexSetCone(dm, c++, cone));
2630             }
2631           }
2632         }
2633       }
2634       PetscCall(DMPlexSymmetrize(dm));
2635       PetscCall(DMPlexStratify(dm));
2636       PetscCall(PetscFree(graph));
2637     } else {
2638       /*
2639         12-21--13
2640          |     |
2641         25  4  24
2642          |     |
2643   12-25--9-16--8-24--13
2644    |     |     |     |
2645   23  5 17  0 15  3  22
2646    |     |     |     |
2647   10-20--6-14--7-19--11
2648          |     |
2649         20  1  19
2650          |     |
2651         10-18--11
2652          |     |
2653         23  2  22
2654          |     |
2655         12-21--13
2656        */
2657       PetscInt cone[4], ornt[4];
2658 
2659       numCells    = rank == 0 ? 6 : 0;
2660       numEdges    = rank == 0 ? 12 : 0;
2661       numVerts    = rank == 0 ? 8 : 0;
2662       firstVertex = numCells;
2663       firstEdge   = numCells + numVerts;
2664       /* Build Topology */
2665       PetscCall(DMPlexSetChart(dm, 0, numCells + numEdges + numVerts));
2666       for (PetscInt c = 0; c < numCells; c++) PetscCall(DMPlexSetConeSize(dm, c, 4));
2667       for (e = firstEdge; e < firstEdge + numEdges; ++e) PetscCall(DMPlexSetConeSize(dm, e, 2));
2668       PetscCall(DMSetUp(dm)); /* Allocate space for cones */
2669       if (rank == 0) {
2670         /* Cell 0 */
2671         cone[0] = 14;
2672         cone[1] = 15;
2673         cone[2] = 16;
2674         cone[3] = 17;
2675         PetscCall(DMPlexSetCone(dm, 0, cone));
2676         ornt[0] = 0;
2677         ornt[1] = 0;
2678         ornt[2] = 0;
2679         ornt[3] = 0;
2680         PetscCall(DMPlexSetConeOrientation(dm, 0, ornt));
2681         /* Cell 1 */
2682         cone[0] = 18;
2683         cone[1] = 19;
2684         cone[2] = 14;
2685         cone[3] = 20;
2686         PetscCall(DMPlexSetCone(dm, 1, cone));
2687         ornt[0] = 0;
2688         ornt[1] = 0;
2689         ornt[2] = -1;
2690         ornt[3] = 0;
2691         PetscCall(DMPlexSetConeOrientation(dm, 1, ornt));
2692         /* Cell 2 */
2693         cone[0] = 21;
2694         cone[1] = 22;
2695         cone[2] = 18;
2696         cone[3] = 23;
2697         PetscCall(DMPlexSetCone(dm, 2, cone));
2698         ornt[0] = 0;
2699         ornt[1] = 0;
2700         ornt[2] = -1;
2701         ornt[3] = 0;
2702         PetscCall(DMPlexSetConeOrientation(dm, 2, ornt));
2703         /* Cell 3 */
2704         cone[0] = 19;
2705         cone[1] = 22;
2706         cone[2] = 24;
2707         cone[3] = 15;
2708         PetscCall(DMPlexSetCone(dm, 3, cone));
2709         ornt[0] = -1;
2710         ornt[1] = -1;
2711         ornt[2] = 0;
2712         ornt[3] = -1;
2713         PetscCall(DMPlexSetConeOrientation(dm, 3, ornt));
2714         /* Cell 4 */
2715         cone[0] = 16;
2716         cone[1] = 24;
2717         cone[2] = 21;
2718         cone[3] = 25;
2719         PetscCall(DMPlexSetCone(dm, 4, cone));
2720         ornt[0] = -1;
2721         ornt[1] = -1;
2722         ornt[2] = -1;
2723         ornt[3] = 0;
2724         PetscCall(DMPlexSetConeOrientation(dm, 4, ornt));
2725         /* Cell 5 */
2726         cone[0] = 20;
2727         cone[1] = 17;
2728         cone[2] = 25;
2729         cone[3] = 23;
2730         PetscCall(DMPlexSetCone(dm, 5, cone));
2731         ornt[0] = -1;
2732         ornt[1] = -1;
2733         ornt[2] = -1;
2734         ornt[3] = -1;
2735         PetscCall(DMPlexSetConeOrientation(dm, 5, ornt));
2736         /* Edges */
2737         cone[0] = 6;
2738         cone[1] = 7;
2739         PetscCall(DMPlexSetCone(dm, 14, cone));
2740         cone[0] = 7;
2741         cone[1] = 8;
2742         PetscCall(DMPlexSetCone(dm, 15, cone));
2743         cone[0] = 8;
2744         cone[1] = 9;
2745         PetscCall(DMPlexSetCone(dm, 16, cone));
2746         cone[0] = 9;
2747         cone[1] = 6;
2748         PetscCall(DMPlexSetCone(dm, 17, cone));
2749         cone[0] = 10;
2750         cone[1] = 11;
2751         PetscCall(DMPlexSetCone(dm, 18, cone));
2752         cone[0] = 11;
2753         cone[1] = 7;
2754         PetscCall(DMPlexSetCone(dm, 19, cone));
2755         cone[0] = 6;
2756         cone[1] = 10;
2757         PetscCall(DMPlexSetCone(dm, 20, cone));
2758         cone[0] = 12;
2759         cone[1] = 13;
2760         PetscCall(DMPlexSetCone(dm, 21, cone));
2761         cone[0] = 13;
2762         cone[1] = 11;
2763         PetscCall(DMPlexSetCone(dm, 22, cone));
2764         cone[0] = 10;
2765         cone[1] = 12;
2766         PetscCall(DMPlexSetCone(dm, 23, cone));
2767         cone[0] = 13;
2768         cone[1] = 8;
2769         PetscCall(DMPlexSetCone(dm, 24, cone));
2770         cone[0] = 12;
2771         cone[1] = 9;
2772         PetscCall(DMPlexSetCone(dm, 25, cone));
2773       }
2774       PetscCall(DMPlexSymmetrize(dm));
2775       PetscCall(DMPlexStratify(dm));
2776       /* Build coordinates */
2777       PetscCall(PetscCalloc1(numVerts * embedDim, &coordsIn));
2778       if (rank == 0) {
2779         coordsIn[0 * embedDim + 0] = -R;
2780         coordsIn[0 * embedDim + 1] = R;
2781         coordsIn[0 * embedDim + 2] = -R;
2782         coordsIn[1 * embedDim + 0] = R;
2783         coordsIn[1 * embedDim + 1] = R;
2784         coordsIn[1 * embedDim + 2] = -R;
2785         coordsIn[2 * embedDim + 0] = R;
2786         coordsIn[2 * embedDim + 1] = -R;
2787         coordsIn[2 * embedDim + 2] = -R;
2788         coordsIn[3 * embedDim + 0] = -R;
2789         coordsIn[3 * embedDim + 1] = -R;
2790         coordsIn[3 * embedDim + 2] = -R;
2791         coordsIn[4 * embedDim + 0] = -R;
2792         coordsIn[4 * embedDim + 1] = R;
2793         coordsIn[4 * embedDim + 2] = R;
2794         coordsIn[5 * embedDim + 0] = R;
2795         coordsIn[5 * embedDim + 1] = R;
2796         coordsIn[5 * embedDim + 2] = R;
2797         coordsIn[6 * embedDim + 0] = -R;
2798         coordsIn[6 * embedDim + 1] = -R;
2799         coordsIn[6 * embedDim + 2] = R;
2800         coordsIn[7 * embedDim + 0] = R;
2801         coordsIn[7 * embedDim + 1] = -R;
2802         coordsIn[7 * embedDim + 2] = R;
2803       }
2804     }
2805     break;
2806   case 3:
2807     if (simplex) {
2808       const PetscReal edgeLen         = 1.0 / PETSC_PHI;
2809       PetscReal       vertexA[4]      = {0.5, 0.5, 0.5, 0.5};
2810       PetscReal       vertexB[4]      = {1.0, 0.0, 0.0, 0.0};
2811       PetscReal       vertexC[4]      = {0.5, 0.5 * PETSC_PHI, 0.5 / PETSC_PHI, 0.0};
2812       const PetscInt  degree          = 12;
2813       PetscInt        s[4]            = {1, 1, 1};
2814       PetscInt        evenPerm[12][4] = {
2815         {0, 1, 2, 3},
2816         {0, 2, 3, 1},
2817         {0, 3, 1, 2},
2818         {1, 0, 3, 2},
2819         {1, 2, 0, 3},
2820         {1, 3, 2, 0},
2821         {2, 0, 1, 3},
2822         {2, 1, 3, 0},
2823         {2, 3, 0, 1},
2824         {3, 0, 2, 1},
2825         {3, 1, 0, 2},
2826         {3, 2, 1, 0}
2827       };
2828       PetscInt  cone[4];
2829       PetscInt *graph, p, i, j, k, l;
2830 
2831       vertexA[0] *= R;
2832       vertexA[1] *= R;
2833       vertexA[2] *= R;
2834       vertexA[3] *= R;
2835       vertexB[0] *= R;
2836       vertexB[1] *= R;
2837       vertexB[2] *= R;
2838       vertexB[3] *= R;
2839       vertexC[0] *= R;
2840       vertexC[1] *= R;
2841       vertexC[2] *= R;
2842       vertexC[3] *= R;
2843       numCells    = rank == 0 ? 600 : 0;
2844       numVerts    = rank == 0 ? 120 : 0;
2845       firstVertex = numCells;
2846       /* Use the 600-cell, which for a unit sphere has coordinates which are
2847 
2848            1/2 (\pm 1, \pm 1,    \pm 1, \pm 1)                          16
2849                (\pm 1,    0,       0,      0)  all cyclic permutations   8
2850            1/2 (\pm 1, \pm phi, \pm 1/phi, 0)  all even permutations    96
2851 
2852          where \phi^2 - \phi - 1 = 0, meaning \phi is the golden ratio \frac{1 + \sqrt{5}}{2}. The edge
2853          length is then given by 1/\phi = 0.61803.
2854 
2855          http://buzzard.pugetsound.edu/sage-practice/ch03s03.html
2856          http://mathworld.wolfram.com/600-Cell.html
2857       */
2858       /* Construct vertices */
2859       PetscCall(PetscCalloc1(numVerts * embedDim, &coordsIn));
2860       i = 0;
2861       if (rank == 0) {
2862         for (s[0] = -1; s[0] < 2; s[0] += 2) {
2863           for (s[1] = -1; s[1] < 2; s[1] += 2) {
2864             for (s[2] = -1; s[2] < 2; s[2] += 2) {
2865               for (s[3] = -1; s[3] < 2; s[3] += 2) {
2866                 for (d = 0; d < embedDim; ++d) coordsIn[i * embedDim + d] = s[d] * vertexA[d];
2867                 ++i;
2868               }
2869             }
2870           }
2871         }
2872         for (p = 0; p < embedDim; ++p) {
2873           s[1] = s[2] = s[3] = 1;
2874           for (s[0] = -1; s[0] < 2; s[0] += 2) {
2875             for (d = 0; d < embedDim; ++d) coordsIn[i * embedDim + d] = s[(d + p) % embedDim] * vertexB[(d + p) % embedDim];
2876             ++i;
2877           }
2878         }
2879         for (p = 0; p < 12; ++p) {
2880           s[3] = 1;
2881           for (s[0] = -1; s[0] < 2; s[0] += 2) {
2882             for (s[1] = -1; s[1] < 2; s[1] += 2) {
2883               for (s[2] = -1; s[2] < 2; s[2] += 2) {
2884                 for (d = 0; d < embedDim; ++d) coordsIn[i * embedDim + d] = s[evenPerm[p][d]] * vertexC[evenPerm[p][d]];
2885                 ++i;
2886               }
2887             }
2888           }
2889         }
2890       }
2891       PetscCheck(i == numVerts, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid 600-cell, vertices %" PetscInt_FMT " != %" PetscInt_FMT, i, numVerts);
2892       /* Construct graph */
2893       PetscCall(PetscCalloc1(numVerts * numVerts, &graph));
2894       for (i = 0; i < numVerts; ++i) {
2895         for (j = 0, k = 0; j < numVerts; ++j) {
2896           if (PetscAbsReal(DiffNormReal(embedDim, &coordsIn[i * embedDim], &coordsIn[j * embedDim]) - edgeLen) < PETSC_SMALL) {
2897             graph[i * numVerts + j] = 1;
2898             ++k;
2899           }
2900         }
2901         PetscCheck(k == degree, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid 600-cell, vertex %" PetscInt_FMT " degree %" PetscInt_FMT " != %" PetscInt_FMT, i, k, degree);
2902       }
2903       /* Build Topology */
2904       PetscCall(DMPlexSetChart(dm, 0, numCells + numVerts));
2905       for (PetscInt c = 0; c < numCells; c++) PetscCall(DMPlexSetConeSize(dm, c, embedDim));
2906       PetscCall(DMSetUp(dm)); /* Allocate space for cones */
2907       /* Cells */
2908       if (rank == 0) {
2909         for (PetscInt i = 0, c = 0; i < numVerts; ++i) {
2910           for (j = 0; j < i; ++j) {
2911             for (k = 0; k < j; ++k) {
2912               for (l = 0; l < k; ++l) {
2913                 if (graph[i * numVerts + j] && graph[j * numVerts + k] && graph[k * numVerts + i] && graph[l * numVerts + i] && graph[l * numVerts + j] && graph[l * numVerts + k]) {
2914                   cone[0] = firstVertex + i;
2915                   cone[1] = firstVertex + j;
2916                   cone[2] = firstVertex + k;
2917                   cone[3] = firstVertex + l;
2918                   /* Check orientation: https://ef.gy/linear-algebra:normal-vectors-in-higher-dimensional-spaces */
2919                   {
2920                     const PetscInt epsilon[4][4][4][4] = {
2921                       {{{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}},  {{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 1}, {0, 0, -1, 0}}, {{0, 0, 0, 0}, {0, 0, 0, -1}, {0, 0, 0, 0}, {0, 1, 0, 0}}, {{0, 0, 0, 0}, {0, 0, 1, 0}, {0, -1, 0, 0}, {0, 0, 0, 0}}},
2922 
2923                       {{{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, -1}, {0, 0, 1, 0}}, {{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}},  {{0, 0, 0, 1}, {0, 0, 0, 0}, {0, 0, 0, 0}, {-1, 0, 0, 0}}, {{0, 0, -1, 0}, {0, 0, 0, 0}, {1, 0, 0, 0}, {0, 0, 0, 0}}},
2924 
2925                       {{{0, 0, 0, 0}, {0, 0, 0, 1}, {0, 0, 0, 0}, {0, -1, 0, 0}}, {{0, 0, 0, -1}, {0, 0, 0, 0}, {0, 0, 0, 0}, {1, 0, 0, 0}}, {{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}},  {{0, 1, 0, 0}, {-1, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}},
2926 
2927                       {{{0, 0, 0, 0}, {0, 0, -1, 0}, {0, 1, 0, 0}, {0, 0, 0, 0}}, {{0, 0, 1, 0}, {0, 0, 0, 0}, {-1, 0, 0, 0}, {0, 0, 0, 0}}, {{0, -1, 0, 0}, {1, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}, {{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}} }
2928                     };
2929                     PetscReal normal[4];
2930                     PetscInt  e, f, g;
2931 
2932                     for (d = 0; d < embedDim; ++d) {
2933                       normal[d] = 0.0;
2934                       for (e = 0; e < embedDim; ++e) {
2935                         for (f = 0; f < embedDim; ++f) {
2936                           for (g = 0; g < embedDim; ++g) {
2937                             normal[d] += epsilon[d][e][f][g] * (coordsIn[j * embedDim + e] - coordsIn[i * embedDim + e]) * (coordsIn[k * embedDim + f] - coordsIn[i * embedDim + f]) * (coordsIn[l * embedDim + f] - coordsIn[i * embedDim + f]);
2938                           }
2939                         }
2940                       }
2941                     }
2942                     if (DotReal(embedDim, normal, &coordsIn[i * embedDim]) < 0) {
2943                       PetscInt tmp = cone[1];
2944                       cone[1]      = cone[2];
2945                       cone[2]      = tmp;
2946                     }
2947                   }
2948                   PetscCall(DMPlexSetCone(dm, c++, cone));
2949                 }
2950               }
2951             }
2952           }
2953         }
2954       }
2955       PetscCall(DMPlexSymmetrize(dm));
2956       PetscCall(DMPlexStratify(dm));
2957       PetscCall(PetscFree(graph));
2958     }
2959     break;
2960   default:
2961     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Unsupported dimension for sphere: %" PetscInt_FMT, dim);
2962   }
2963   /* Create coordinates */
2964   PetscCall(DMGetCoordinateSection(dm, &coordSection));
2965   PetscCall(PetscSectionSetNumFields(coordSection, 1));
2966   PetscCall(PetscSectionSetFieldComponents(coordSection, 0, embedDim));
2967   PetscCall(PetscSectionSetChart(coordSection, firstVertex, firstVertex + numVerts));
2968   for (v = firstVertex; v < firstVertex + numVerts; ++v) {
2969     PetscCall(PetscSectionSetDof(coordSection, v, embedDim));
2970     PetscCall(PetscSectionSetFieldDof(coordSection, v, 0, embedDim));
2971   }
2972   PetscCall(PetscSectionSetUp(coordSection));
2973   PetscCall(PetscSectionGetStorageSize(coordSection, &coordSize));
2974   PetscCall(VecCreate(PETSC_COMM_SELF, &coordinates));
2975   PetscCall(VecSetBlockSize(coordinates, embedDim));
2976   PetscCall(PetscObjectSetName((PetscObject)coordinates, "coordinates"));
2977   PetscCall(VecSetSizes(coordinates, coordSize, PETSC_DETERMINE));
2978   PetscCall(VecSetType(coordinates, VECSTANDARD));
2979   PetscCall(VecGetArray(coordinates, &coords));
2980   for (v = 0; v < numVerts; ++v)
2981     for (d = 0; d < embedDim; ++d) coords[v * embedDim + d] = coordsIn[v * embedDim + d];
2982   PetscCall(VecRestoreArray(coordinates, &coords));
2983   PetscCall(DMSetCoordinatesLocal(dm, coordinates));
2984   PetscCall(VecDestroy(&coordinates));
2985   PetscCall(PetscFree(coordsIn));
2986   {
2987     DM          cdm;
2988     PetscDS     cds;
2989     PetscScalar c = R;
2990 
2991     PetscCall(DMPlexCreateCoordinateSpace(dm, 1, PETSC_TRUE, snapToSphere));
2992     PetscCall(DMGetCoordinateDM(dm, &cdm));
2993     PetscCall(DMGetDS(cdm, &cds));
2994     PetscCall(PetscDSSetConstants(cds, 1, &c));
2995   }
2996   PetscCall(PetscLogEventEnd(DMPLEX_Generate, dm, 0, 0, 0));
2997   /* Wait for coordinate creation before doing in-place modification */
2998   if (simplex) PetscCall(DMPlexInterpolateInPlace_Internal(dm));
2999   PetscFunctionReturn(PETSC_SUCCESS);
3000 }
3001 
3002 typedef void (*TPSEvaluateFunc)(const PetscReal[], PetscReal *, PetscReal[], PetscReal (*)[3]);
3003 
3004 /*
3005  The Schwarz P implicit surface is
3006 
3007      f(x) = cos(x0) + cos(x1) + cos(x2) = 0
3008 */
3009 static void TPSEvaluate_SchwarzP(const PetscReal y[3], PetscReal *f, PetscReal grad[], PetscReal (*hess)[3])
3010 {
3011   PetscReal c[3] = {PetscCosReal(y[0] * PETSC_PI), PetscCosReal(y[1] * PETSC_PI), PetscCosReal(y[2] * PETSC_PI)};
3012   PetscReal g[3] = {-PetscSinReal(y[0] * PETSC_PI), -PetscSinReal(y[1] * PETSC_PI), -PetscSinReal(y[2] * PETSC_PI)};
3013   f[0]           = c[0] + c[1] + c[2];
3014   for (PetscInt i = 0; i < 3; i++) {
3015     grad[i] = PETSC_PI * g[i];
3016     for (PetscInt j = 0; j < 3; j++) hess[i][j] = (i == j) ? -PetscSqr(PETSC_PI) * c[i] : 0.;
3017   }
3018 }
3019 
3020 // u[] is a tentative normal on input. Replace with the implicit function gradient in the same direction
3021 static PetscErrorCode TPSExtrudeNormalFunc_SchwarzP(PetscInt dim, PetscReal time, const PetscReal x[], PetscInt r, PetscScalar u[], void *ctx)
3022 {
3023   for (PetscInt i = 0; i < 3; i++) u[i] = -PETSC_PI * PetscSinReal(x[i] * PETSC_PI);
3024   return PETSC_SUCCESS;
3025 }
3026 
3027 /*
3028  The Gyroid implicit surface is
3029 
3030  f(x,y,z) = sin(pi * x) * cos (pi * (y + 1/2))  + sin(pi * (y + 1/2)) * cos(pi * (z + 1/4)) + sin(pi * (z + 1/4)) * cos(pi * x)
3031 
3032 */
3033 static void TPSEvaluate_Gyroid(const PetscReal y[3], PetscReal *f, PetscReal grad[], PetscReal (*hess)[3])
3034 {
3035   PetscReal s[3] = {PetscSinReal(PETSC_PI * y[0]), PetscSinReal(PETSC_PI * (y[1] + .5)), PetscSinReal(PETSC_PI * (y[2] + .25))};
3036   PetscReal c[3] = {PetscCosReal(PETSC_PI * y[0]), PetscCosReal(PETSC_PI * (y[1] + .5)), PetscCosReal(PETSC_PI * (y[2] + .25))};
3037   f[0]           = s[0] * c[1] + s[1] * c[2] + s[2] * c[0];
3038   grad[0]        = PETSC_PI * (c[0] * c[1] - s[2] * s[0]);
3039   grad[1]        = PETSC_PI * (c[1] * c[2] - s[0] * s[1]);
3040   grad[2]        = PETSC_PI * (c[2] * c[0] - s[1] * s[2]);
3041   hess[0][0]     = -PetscSqr(PETSC_PI) * (s[0] * c[1] + s[2] * c[0]);
3042   hess[0][1]     = -PetscSqr(PETSC_PI) * (c[0] * s[1]);
3043   hess[0][2]     = -PetscSqr(PETSC_PI) * (c[2] * s[0]);
3044   hess[1][0]     = -PetscSqr(PETSC_PI) * (s[1] * c[2] + s[0] * c[1]);
3045   hess[1][1]     = -PetscSqr(PETSC_PI) * (c[1] * s[2]);
3046   hess[2][2]     = -PetscSqr(PETSC_PI) * (c[0] * s[1]);
3047   hess[2][0]     = -PetscSqr(PETSC_PI) * (s[2] * c[0] + s[1] * c[2]);
3048   hess[2][1]     = -PetscSqr(PETSC_PI) * (c[2] * s[0]);
3049   hess[2][2]     = -PetscSqr(PETSC_PI) * (c[1] * s[2]);
3050 }
3051 
3052 // u[] is a tentative normal on input. Replace with the implicit function gradient in the same direction
3053 static PetscErrorCode TPSExtrudeNormalFunc_Gyroid(PetscInt dim, PetscReal time, const PetscReal x[], PetscInt r, PetscScalar u[], void *ctx)
3054 {
3055   PetscReal s[3] = {PetscSinReal(PETSC_PI * x[0]), PetscSinReal(PETSC_PI * (x[1] + .5)), PetscSinReal(PETSC_PI * (x[2] + .25))};
3056   PetscReal c[3] = {PetscCosReal(PETSC_PI * x[0]), PetscCosReal(PETSC_PI * (x[1] + .5)), PetscCosReal(PETSC_PI * (x[2] + .25))};
3057   u[0]           = PETSC_PI * (c[0] * c[1] - s[2] * s[0]);
3058   u[1]           = PETSC_PI * (c[1] * c[2] - s[0] * s[1]);
3059   u[2]           = PETSC_PI * (c[2] * c[0] - s[1] * s[2]);
3060   return PETSC_SUCCESS;
3061 }
3062 
3063 /*
3064    We wish to solve
3065 
3066          min_y || y - x ||^2  subject to f(y) = 0
3067 
3068    Let g(y) = grad(f).  The minimization problem is equivalent to asking to satisfy
3069    f(y) = 0 and (y-x) is parallel to g(y).  We do this by using Householder QR to obtain a basis for the
3070    tangent space and ask for both components in the tangent space to be zero.
3071 
3072    Take g to be a column vector and compute the "full QR" factorization Q R = g,
3073    where Q = I - 2 n n^T is a symmetric orthogonal matrix.
3074    The first column of Q is parallel to g so the remaining two columns span the null space.
3075    Let Qn = Q[:,1:] be those remaining columns.  Then Qn Qn^T is an orthogonal projector into the tangent space.
3076    Since Q is symmetric, this is equivalent to multiplying by Q and taking the last two entries.
3077    In total, we have a system of 3 equations in 3 unknowns:
3078 
3079      f(y) = 0                       1 equation
3080      Qn^T (y - x) = 0               2 equations
3081 
3082    Here, we compute the residual and Jacobian of this system.
3083 */
3084 static void TPSNearestPointResJac(TPSEvaluateFunc feval, const PetscScalar x[], const PetscScalar y[], PetscScalar res[], PetscScalar J[])
3085 {
3086   PetscReal yreal[3] = {PetscRealPart(y[0]), PetscRealPart(y[1]), PetscRealPart(y[2])};
3087   PetscReal d[3]     = {PetscRealPart(y[0] - x[0]), PetscRealPart(y[1] - x[1]), PetscRealPart(y[2] - x[2])};
3088   PetscReal f, grad[3], n[3], norm, norm_y[3], nd, nd_y[3], sign;
3089   PetscReal n_y[3][3] = {
3090     {0, 0, 0},
3091     {0, 0, 0},
3092     {0, 0, 0}
3093   };
3094 
3095   feval(yreal, &f, grad, n_y);
3096 
3097   for (PetscInt i = 0; i < 3; i++) n[i] = grad[i];
3098   norm = PetscSqrtReal(PetscSqr(n[0]) + PetscSqr(n[1]) + PetscSqr(n[2]));
3099   for (PetscInt i = 0; i < 3; i++) norm_y[i] = 1. / norm * n[i] * n_y[i][i];
3100 
3101   // Define the Householder reflector
3102   sign = n[0] >= 0 ? 1. : -1.;
3103   n[0] += norm * sign;
3104   for (PetscInt i = 0; i < 3; i++) n_y[0][i] += norm_y[i] * sign;
3105 
3106   norm      = PetscSqrtReal(PetscSqr(n[0]) + PetscSqr(n[1]) + PetscSqr(n[2]));
3107   norm_y[0] = 1. / norm * (n[0] * n_y[0][0]);
3108   norm_y[1] = 1. / norm * (n[0] * n_y[0][1] + n[1] * n_y[1][1]);
3109   norm_y[2] = 1. / norm * (n[0] * n_y[0][2] + n[2] * n_y[2][2]);
3110 
3111   for (PetscInt i = 0; i < 3; i++) {
3112     n[i] /= norm;
3113     for (PetscInt j = 0; j < 3; j++) {
3114       // note that n[i] is n_old[i]/norm when executing the code below
3115       n_y[i][j] = n_y[i][j] / norm - n[i] / norm * norm_y[j];
3116     }
3117   }
3118 
3119   nd = n[0] * d[0] + n[1] * d[1] + n[2] * d[2];
3120   for (PetscInt i = 0; i < 3; i++) nd_y[i] = n[i] + n_y[0][i] * d[0] + n_y[1][i] * d[1] + n_y[2][i] * d[2];
3121 
3122   res[0] = f;
3123   res[1] = d[1] - 2 * n[1] * nd;
3124   res[2] = d[2] - 2 * n[2] * nd;
3125   // J[j][i] is J_{ij} (column major)
3126   for (PetscInt j = 0; j < 3; j++) {
3127     J[0 + j * 3] = grad[j];
3128     J[1 + j * 3] = (j == 1) * 1. - 2 * (n_y[1][j] * nd + n[1] * nd_y[j]);
3129     J[2 + j * 3] = (j == 2) * 1. - 2 * (n_y[2][j] * nd + n[2] * nd_y[j]);
3130   }
3131 }
3132 
3133 /*
3134    Project x to the nearest point on the implicit surface using Newton's method.
3135 */
3136 static PetscErrorCode TPSNearestPoint(TPSEvaluateFunc feval, PetscScalar x[])
3137 {
3138   PetscScalar y[3] = {x[0], x[1], x[2]}; // Initial guess
3139 
3140   PetscFunctionBegin;
3141   for (PetscInt iter = 0; iter < 10; iter++) {
3142     PetscScalar res[3], J[9];
3143     PetscReal   resnorm;
3144     TPSNearestPointResJac(feval, x, y, res, J);
3145     resnorm = PetscSqrtReal(PetscSqr(PetscRealPart(res[0])) + PetscSqr(PetscRealPart(res[1])) + PetscSqr(PetscRealPart(res[2])));
3146     if (0) { // Turn on this monitor if you need to confirm quadratic convergence
3147       PetscCall(PetscPrintf(PETSC_COMM_SELF, "[%" PetscInt_FMT "] res [%g %g %g]\n", iter, (double)PetscRealPart(res[0]), (double)PetscRealPart(res[1]), (double)PetscRealPart(res[2])));
3148     }
3149     if (resnorm < PETSC_SMALL) break;
3150 
3151     // Take the Newton step
3152     PetscCall(PetscKernel_A_gets_inverse_A_3(J, 0., PETSC_FALSE, NULL));
3153     PetscKernel_v_gets_v_minus_A_times_w_3(y, J, res);
3154   }
3155   for (PetscInt i = 0; i < 3; i++) x[i] = y[i];
3156   PetscFunctionReturn(PETSC_SUCCESS);
3157 }
3158 
3159 const char *const DMPlexTPSTypes[] = {"SCHWARZ_P", "GYROID", "DMPlexTPSType", "DMPLEX_TPS_", NULL};
3160 
3161 static PetscErrorCode DMPlexCreateTPSMesh_Internal(DM dm, DMPlexTPSType tpstype, const PetscInt extent[], const DMBoundaryType periodic[], PetscBool tps_distribute, PetscInt refinements, PetscInt layers, PetscReal thickness)
3162 {
3163   PetscMPIInt rank;
3164   PetscInt    topoDim = 2, spaceDim = 3, numFaces = 0, numVertices = 0, numEdges = 0;
3165   PetscInt(*edges)[2] = NULL, *edgeSets = NULL;
3166   PetscInt           *cells_flat = NULL;
3167   PetscReal          *vtxCoords  = NULL;
3168   TPSEvaluateFunc     evalFunc   = NULL;
3169   PetscSimplePointFn *normalFunc = NULL;
3170   DMLabel             label;
3171 
3172   PetscFunctionBegin;
3173   PetscCall(PetscLogEventBegin(DMPLEX_Generate, dm, 0, 0, 0));
3174   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
3175   PetscCheck((layers != 0) ^ (thickness == 0.), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_INCOMP, "Layers %" PetscInt_FMT " must be nonzero iff thickness %g is nonzero", layers, (double)thickness);
3176   switch (tpstype) {
3177   case DMPLEX_TPS_SCHWARZ_P:
3178     PetscCheck(!periodic || (periodic[0] == DM_BOUNDARY_NONE && periodic[1] == DM_BOUNDARY_NONE && periodic[2] == DM_BOUNDARY_NONE), PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Schwarz P does not support periodic meshes");
3179     if (rank == 0) {
3180       PetscInt(*cells)[6][4][4] = NULL; // [junction, junction-face, cell, conn]
3181       PetscInt  Njunctions = 0, Ncuts = 0, Npipes[3], vcount;
3182       PetscReal L = 1;
3183 
3184       Npipes[0]   = (extent[0] + 1) * extent[1] * extent[2];
3185       Npipes[1]   = extent[0] * (extent[1] + 1) * extent[2];
3186       Npipes[2]   = extent[0] * extent[1] * (extent[2] + 1);
3187       Njunctions  = extent[0] * extent[1] * extent[2];
3188       Ncuts       = 2 * (extent[0] * extent[1] + extent[1] * extent[2] + extent[2] * extent[0]);
3189       numVertices = 4 * (Npipes[0] + Npipes[1] + Npipes[2]) + 8 * Njunctions;
3190       PetscCall(PetscMalloc1(3 * numVertices, &vtxCoords));
3191       PetscCall(PetscMalloc1(Njunctions, &cells));
3192       PetscCall(PetscMalloc1(Ncuts * 4, &edges));
3193       PetscCall(PetscMalloc1(Ncuts * 4, &edgeSets));
3194       // x-normal pipes
3195       vcount = 0;
3196       for (PetscInt i = 0; i < extent[0] + 1; i++) {
3197         for (PetscInt j = 0; j < extent[1]; j++) {
3198           for (PetscInt k = 0; k < extent[2]; k++) {
3199             for (PetscInt l = 0; l < 4; l++) {
3200               vtxCoords[vcount++] = (2 * i - 1) * L;
3201               vtxCoords[vcount++] = 2 * j * L + PetscCosReal((2 * l + 1) * PETSC_PI / 4) * L / 2;
3202               vtxCoords[vcount++] = 2 * k * L + PetscSinReal((2 * l + 1) * PETSC_PI / 4) * L / 2;
3203             }
3204           }
3205         }
3206       }
3207       // y-normal pipes
3208       for (PetscInt i = 0; i < extent[0]; i++) {
3209         for (PetscInt j = 0; j < extent[1] + 1; j++) {
3210           for (PetscInt k = 0; k < extent[2]; k++) {
3211             for (PetscInt l = 0; l < 4; l++) {
3212               vtxCoords[vcount++] = 2 * i * L + PetscSinReal((2 * l + 1) * PETSC_PI / 4) * L / 2;
3213               vtxCoords[vcount++] = (2 * j - 1) * L;
3214               vtxCoords[vcount++] = 2 * k * L + PetscCosReal((2 * l + 1) * PETSC_PI / 4) * L / 2;
3215             }
3216           }
3217         }
3218       }
3219       // z-normal pipes
3220       for (PetscInt i = 0; i < extent[0]; i++) {
3221         for (PetscInt j = 0; j < extent[1]; j++) {
3222           for (PetscInt k = 0; k < extent[2] + 1; k++) {
3223             for (PetscInt l = 0; l < 4; l++) {
3224               vtxCoords[vcount++] = 2 * i * L + PetscCosReal((2 * l + 1) * PETSC_PI / 4) * L / 2;
3225               vtxCoords[vcount++] = 2 * j * L + PetscSinReal((2 * l + 1) * PETSC_PI / 4) * L / 2;
3226               vtxCoords[vcount++] = (2 * k - 1) * L;
3227             }
3228           }
3229         }
3230       }
3231       // junctions
3232       for (PetscInt i = 0; i < extent[0]; i++) {
3233         for (PetscInt j = 0; j < extent[1]; j++) {
3234           for (PetscInt k = 0; k < extent[2]; k++) {
3235             const PetscInt J = (i * extent[1] + j) * extent[2] + k, Jvoff = (Npipes[0] + Npipes[1] + Npipes[2]) * 4 + J * 8;
3236             PetscCheck(vcount / 3 == Jvoff, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected vertex count");
3237             for (PetscInt ii = 0; ii < 2; ii++) {
3238               for (PetscInt jj = 0; jj < 2; jj++) {
3239                 for (PetscInt kk = 0; kk < 2; kk++) {
3240                   double Ls           = (1 - sqrt(2) / 4) * L;
3241                   vtxCoords[vcount++] = 2 * i * L + (2 * ii - 1) * Ls;
3242                   vtxCoords[vcount++] = 2 * j * L + (2 * jj - 1) * Ls;
3243                   vtxCoords[vcount++] = 2 * k * L + (2 * kk - 1) * Ls;
3244                 }
3245               }
3246             }
3247             const PetscInt jfaces[3][2][4] = {
3248               {{3, 1, 0, 2}, {7, 5, 4, 6}}, // x-aligned
3249               {{5, 4, 0, 1}, {7, 6, 2, 3}}, // y-aligned
3250               {{6, 2, 0, 4}, {7, 3, 1, 5}}  // z-aligned
3251             };
3252             const PetscInt pipe_lo[3] = {// vertex numbers of pipes
3253                                          ((i * extent[1] + j) * extent[2] + k) * 4, ((i * (extent[1] + 1) + j) * extent[2] + k + Npipes[0]) * 4, ((i * extent[1] + j) * (extent[2] + 1) + k + Npipes[0] + Npipes[1]) * 4};
3254             const PetscInt pipe_hi[3] = {// vertex numbers of pipes
3255                                          (((i + 1) * extent[1] + j) * extent[2] + k) * 4, ((i * (extent[1] + 1) + j + 1) * extent[2] + k + Npipes[0]) * 4, ((i * extent[1] + j) * (extent[2] + 1) + k + 1 + Npipes[0] + Npipes[1]) * 4};
3256             for (PetscInt dir = 0; dir < 3; dir++) { // x,y,z
3257               const PetscInt ijk[3] = {i, j, k};
3258               for (PetscInt l = 0; l < 4; l++) { // rotations
3259                 cells[J][dir * 2 + 0][l][0] = pipe_lo[dir] + l;
3260                 cells[J][dir * 2 + 0][l][1] = Jvoff + jfaces[dir][0][l];
3261                 cells[J][dir * 2 + 0][l][2] = Jvoff + jfaces[dir][0][(l - 1 + 4) % 4];
3262                 cells[J][dir * 2 + 0][l][3] = pipe_lo[dir] + (l - 1 + 4) % 4;
3263                 cells[J][dir * 2 + 1][l][0] = Jvoff + jfaces[dir][1][l];
3264                 cells[J][dir * 2 + 1][l][1] = pipe_hi[dir] + l;
3265                 cells[J][dir * 2 + 1][l][2] = pipe_hi[dir] + (l - 1 + 4) % 4;
3266                 cells[J][dir * 2 + 1][l][3] = Jvoff + jfaces[dir][1][(l - 1 + 4) % 4];
3267                 if (ijk[dir] == 0) {
3268                   edges[numEdges][0] = pipe_lo[dir] + l;
3269                   edges[numEdges][1] = pipe_lo[dir] + (l + 1) % 4;
3270                   edgeSets[numEdges] = dir * 2 + 1;
3271                   numEdges++;
3272                 }
3273                 if (ijk[dir] + 1 == extent[dir]) {
3274                   edges[numEdges][0] = pipe_hi[dir] + l;
3275                   edges[numEdges][1] = pipe_hi[dir] + (l + 1) % 4;
3276                   edgeSets[numEdges] = dir * 2 + 2;
3277                   numEdges++;
3278                 }
3279               }
3280             }
3281           }
3282         }
3283       }
3284       PetscCheck(numEdges == Ncuts * 4, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Edge count %" PetscInt_FMT " incompatible with number of cuts %" PetscInt_FMT, numEdges, Ncuts);
3285       numFaces   = 24 * Njunctions;
3286       cells_flat = cells[0][0][0];
3287     }
3288     evalFunc   = TPSEvaluate_SchwarzP;
3289     normalFunc = TPSExtrudeNormalFunc_SchwarzP;
3290     break;
3291   case DMPLEX_TPS_GYROID:
3292     if (rank == 0) {
3293       // This is a coarse mesh approximation of the gyroid shifted to being the zero of the level set
3294       //
3295       //     sin(pi*x)*cos(pi*(y+1/2)) + sin(pi*(y+1/2))*cos(pi*(z+1/4)) + sin(pi*(z+1/4))*cos(x)
3296       //
3297       // on the cell [0,2]^3.
3298       //
3299       // Think about dividing that cell into four columns, and focus on the column [0,1]x[0,1]x[0,2].
3300       // If you looked at the gyroid in that column at different slices of z you would see that it kind of spins
3301       // like a boomerang:
3302       //
3303       //     z = 0          z = 1/4        z = 1/2        z = 3/4     //
3304       //     -----          -------        -------        -------     //
3305       //                                                              //
3306       //     +       +      +       +      +       +      +   \   +   //
3307       //      \                                   /            \      //
3308       //       \            `-_   _-'            /              }     //
3309       //        *-_            `-'            _-'              /      //
3310       //     +     `-+      +       +      +-'     +      +   /   +   //
3311       //                                                              //
3312       //                                                              //
3313       //     z = 1          z = 5/4        z = 3/2        z = 7/4     //
3314       //     -----          -------        -------        -------     //
3315       //                                                              //
3316       //     +-_     +      +       +      +     _-+      +   /   +   //
3317       //        `-_            _-_            _-`            /        //
3318       //           \        _-'   `-_        /              {         //
3319       //            \                       /                \        //
3320       //     +       +      +       +      +       +      +   \   +   //
3321       //
3322       //
3323       // This course mesh approximates each of these slices by two line segments,
3324       // and then connects the segments in consecutive layers with quadrilateral faces.
3325       // All of the end points of the segments are multiples of 1/4 except for the
3326       // point * in the picture for z = 0 above and the similar points in other layers.
3327       // That point is at (gamma, gamma, 0), where gamma is calculated below.
3328       //
3329       // The column  [1,2]x[1,2]x[0,2] looks the same as this column;
3330       // The columns [1,2]x[0,1]x[0,2] and [0,1]x[1,2]x[0,2] are mirror images.
3331       //
3332       // As for how this method turned into the names given to the vertices:
3333       // that was not systematic, it was just the way it worked out in my handwritten notes.
3334 
3335       PetscInt facesPerBlock = 64;
3336       PetscInt vertsPerBlock = 56;
3337       PetscInt extentPlus[3];
3338       PetscInt numBlocks, numBlocksPlus;
3339       const PetscInt A = 0, B = 1, C = 2, D = 3, E = 4, F = 5, G = 6, H = 7, II = 8, J = 9, K = 10, L = 11, M = 12, N = 13, O = 14, P = 15, Q = 16, R = 17, S = 18, T = 19, U = 20, V = 21, W = 22, X = 23, Y = 24, Z = 25, Ap = 26, Bp = 27, Cp = 28, Dp = 29, Ep = 30, Fp = 31, Gp = 32, Hp = 33, Ip = 34, Jp = 35, Kp = 36, Lp = 37, Mp = 38, Np = 39, Op = 40, Pp = 41, Qp = 42, Rp = 43, Sp = 44, Tp = 45, Up = 46, Vp = 47, Wp = 48, Xp = 49, Yp = 50, Zp = 51, Aq = 52, Bq = 53, Cq = 54, Dq = 55;
3340       const PetscInt pattern[64][4] = {
3341         /* face to vertex within the coarse discretization of a single gyroid block */
3342         /* layer 0 */
3343         {A,           C,           K,           G          },
3344         {C,           B,           II,          K          },
3345         {D,           A,           H,           L          },
3346         {B + 56 * 1,  D,           L,           J          },
3347         {E,           B + 56 * 1,  J,           N          },
3348         {A + 56 * 2,  E,           N,           H + 56 * 2 },
3349         {F,           A + 56 * 2,  G + 56 * 2,  M          },
3350         {B,           F,           M,           II         },
3351         /* layer 1 */
3352         {G,           K,           Q,           O          },
3353         {K,           II,          P,           Q          },
3354         {L,           H,           O + 56 * 1,  R          },
3355         {J,           L,           R,           P          },
3356         {N,           J,           P,           S          },
3357         {H + 56 * 2,  N,           S,           O + 56 * 3 },
3358         {M,           G + 56 * 2,  O + 56 * 2,  T          },
3359         {II,          M,           T,           P          },
3360         /* layer 2 */
3361         {O,           Q,           Y,           U          },
3362         {Q,           P,           W,           Y          },
3363         {R,           O + 56 * 1,  U + 56 * 1,  Ap         },
3364         {P,           R,           Ap,          W          },
3365         {S,           P,           X,           Bp         },
3366         {O + 56 * 3,  S,           Bp,          V + 56 * 1 },
3367         {T,           O + 56 * 2,  V,           Z          },
3368         {P,           T,           Z,           X          },
3369         /* layer 3 */
3370         {U,           Y,           Ep,          Dp         },
3371         {Y,           W,           Cp,          Ep         },
3372         {Ap,          U + 56 * 1,  Dp + 56 * 1, Gp         },
3373         {W,           Ap,          Gp,          Cp         },
3374         {Bp,          X,           Cp + 56 * 2, Fp         },
3375         {V + 56 * 1,  Bp,          Fp,          Dp + 56 * 1},
3376         {Z,           V,           Dp,          Hp         },
3377         {X,           Z,           Hp,          Cp + 56 * 2},
3378         /* layer 4 */
3379         {Dp,          Ep,          Mp,          Kp         },
3380         {Ep,          Cp,          Ip,          Mp         },
3381         {Gp,          Dp + 56 * 1, Lp,          Np         },
3382         {Cp,          Gp,          Np,          Jp         },
3383         {Fp,          Cp + 56 * 2, Jp + 56 * 2, Pp         },
3384         {Dp + 56 * 1, Fp,          Pp,          Lp         },
3385         {Hp,          Dp,          Kp,          Op         },
3386         {Cp + 56 * 2, Hp,          Op,          Ip + 56 * 2},
3387         /* layer 5 */
3388         {Kp,          Mp,          Sp,          Rp         },
3389         {Mp,          Ip,          Qp,          Sp         },
3390         {Np,          Lp,          Rp,          Tp         },
3391         {Jp,          Np,          Tp,          Qp + 56 * 1},
3392         {Pp,          Jp + 56 * 2, Qp + 56 * 3, Up         },
3393         {Lp,          Pp,          Up,          Rp         },
3394         {Op,          Kp,          Rp,          Vp         },
3395         {Ip + 56 * 2, Op,          Vp,          Qp + 56 * 2},
3396         /* layer 6 */
3397         {Rp,          Sp,          Aq,          Yp         },
3398         {Sp,          Qp,          Wp,          Aq         },
3399         {Tp,          Rp,          Yp,          Cq         },
3400         {Qp + 56 * 1, Tp,          Cq,          Wp + 56 * 1},
3401         {Up,          Qp + 56 * 3, Xp + 56 * 1, Dq         },
3402         {Rp,          Up,          Dq,          Zp         },
3403         {Vp,          Rp,          Zp,          Bq         },
3404         {Qp + 56 * 2, Vp,          Bq,          Xp         },
3405         /* layer 7 (the top is the periodic image of the bottom of layer 0) */
3406         {Yp,          Aq,          C + 56 * 4,  A + 56 * 4 },
3407         {Aq,          Wp,          B + 56 * 4,  C + 56 * 4 },
3408         {Cq,          Yp,          A + 56 * 4,  D + 56 * 4 },
3409         {Wp + 56 * 1, Cq,          D + 56 * 4,  B + 56 * 5 },
3410         {Dq,          Xp + 56 * 1, B + 56 * 5,  E + 56 * 4 },
3411         {Zp,          Dq,          E + 56 * 4,  A + 56 * 6 },
3412         {Bq,          Zp,          A + 56 * 6,  F + 56 * 4 },
3413         {Xp,          Bq,          F + 56 * 4,  B + 56 * 4 }
3414       };
3415       const PetscReal gamma                = PetscAcosReal((PetscSqrtReal(3.) - 1.) / PetscSqrtReal(2.)) / PETSC_PI;
3416       const PetscReal patternCoords[56][3] = {
3417         {1.,        0.,        0.  }, /* A  */
3418         {0.,        1.,        0.  }, /* B  */
3419         {gamma,     gamma,     0.  }, /* C  */
3420         {1 + gamma, 1 - gamma, 0.  }, /* D  */
3421         {2 - gamma, 2 - gamma, 0.  }, /* E  */
3422         {1 - gamma, 1 + gamma, 0.  }, /* F  */
3423 
3424         {.5,        0,         .25 }, /* G  */
3425         {1.5,       0.,        .25 }, /* H  */
3426         {.5,        1.,        .25 }, /* II */
3427         {1.5,       1.,        .25 }, /* J  */
3428         {.25,       .5,        .25 }, /* K  */
3429         {1.25,      .5,        .25 }, /* L  */
3430         {.75,       1.5,       .25 }, /* M  */
3431         {1.75,      1.5,       .25 }, /* N  */
3432 
3433         {0.,        0.,        .5  }, /* O  */
3434         {1.,        1.,        .5  }, /* P  */
3435         {gamma,     1 - gamma, .5  }, /* Q  */
3436         {1 + gamma, gamma,     .5  }, /* R  */
3437         {2 - gamma, 1 + gamma, .5  }, /* S  */
3438         {1 - gamma, 2 - gamma, .5  }, /* T  */
3439 
3440         {0.,        .5,        .75 }, /* U  */
3441         {0.,        1.5,       .75 }, /* V  */
3442         {1.,        .5,        .75 }, /* W  */
3443         {1.,        1.5,       .75 }, /* X  */
3444         {.5,        .75,       .75 }, /* Y  */
3445         {.5,        1.75,      .75 }, /* Z  */
3446         {1.5,       .25,       .75 }, /* Ap */
3447         {1.5,       1.25,      .75 }, /* Bp */
3448 
3449         {1.,        0.,        1.  }, /* Cp */
3450         {0.,        1.,        1.  }, /* Dp */
3451         {1 - gamma, 1 - gamma, 1.  }, /* Ep */
3452         {1 + gamma, 1 + gamma, 1.  }, /* Fp */
3453         {2 - gamma, gamma,     1.  }, /* Gp */
3454         {gamma,     2 - gamma, 1.  }, /* Hp */
3455 
3456         {.5,        0.,        1.25}, /* Ip */
3457         {1.5,       0.,        1.25}, /* Jp */
3458         {.5,        1.,        1.25}, /* Kp */
3459         {1.5,       1.,        1.25}, /* Lp */
3460         {.75,       .5,        1.25}, /* Mp */
3461         {1.75,      .5,        1.25}, /* Np */
3462         {.25,       1.5,       1.25}, /* Op */
3463         {1.25,      1.5,       1.25}, /* Pp */
3464 
3465         {0.,        0.,        1.5 }, /* Qp */
3466         {1.,        1.,        1.5 }, /* Rp */
3467         {1 - gamma, gamma,     1.5 }, /* Sp */
3468         {2 - gamma, 1 - gamma, 1.5 }, /* Tp */
3469         {1 + gamma, 2 - gamma, 1.5 }, /* Up */
3470         {gamma,     1 + gamma, 1.5 }, /* Vp */
3471 
3472         {0.,        .5,        1.75}, /* Wp */
3473         {0.,        1.5,       1.75}, /* Xp */
3474         {1.,        .5,        1.75}, /* Yp */
3475         {1.,        1.5,       1.75}, /* Zp */
3476         {.5,        .25,       1.75}, /* Aq */
3477         {.5,        1.25,      1.75}, /* Bq */
3478         {1.5,       .75,       1.75}, /* Cq */
3479         {1.5,       1.75,      1.75}, /* Dq */
3480       };
3481       PetscInt(*cells)[64][4] = NULL;
3482       PetscBool *seen;
3483       PetscInt  *vertToTrueVert;
3484       PetscInt   count;
3485 
3486       for (PetscInt i = 0; i < 3; i++) extentPlus[i] = extent[i] + 1;
3487       numBlocks = 1;
3488       for (PetscInt i = 0; i < 3; i++) numBlocks *= extent[i];
3489       numBlocksPlus = 1;
3490       for (PetscInt i = 0; i < 3; i++) numBlocksPlus *= extentPlus[i];
3491       numFaces = numBlocks * facesPerBlock;
3492       PetscCall(PetscMalloc1(numBlocks, &cells));
3493       PetscCall(PetscCalloc1(numBlocksPlus * vertsPerBlock, &seen));
3494       for (PetscInt k = 0; k < extent[2]; k++) {
3495         for (PetscInt j = 0; j < extent[1]; j++) {
3496           for (PetscInt i = 0; i < extent[0]; i++) {
3497             for (PetscInt f = 0; f < facesPerBlock; f++) {
3498               for (PetscInt v = 0; v < 4; v++) {
3499                 PetscInt vertRaw     = pattern[f][v];
3500                 PetscInt blockidx    = vertRaw / 56;
3501                 PetscInt patternvert = vertRaw % 56;
3502                 PetscInt xplus       = (blockidx & 1);
3503                 PetscInt yplus       = (blockidx & 2) >> 1;
3504                 PetscInt zplus       = (blockidx & 4) >> 2;
3505                 PetscInt zcoord      = (periodic && periodic[2] == DM_BOUNDARY_PERIODIC) ? ((k + zplus) % extent[2]) : (k + zplus);
3506                 PetscInt ycoord      = (periodic && periodic[1] == DM_BOUNDARY_PERIODIC) ? ((j + yplus) % extent[1]) : (j + yplus);
3507                 PetscInt xcoord      = (periodic && periodic[0] == DM_BOUNDARY_PERIODIC) ? ((i + xplus) % extent[0]) : (i + xplus);
3508                 PetscInt vert        = ((zcoord * extentPlus[1] + ycoord) * extentPlus[0] + xcoord) * 56 + patternvert;
3509 
3510                 cells[(k * extent[1] + j) * extent[0] + i][f][v] = vert;
3511                 seen[vert]                                       = PETSC_TRUE;
3512               }
3513             }
3514           }
3515         }
3516       }
3517       for (PetscInt i = 0; i < numBlocksPlus * vertsPerBlock; i++)
3518         if (seen[i]) numVertices++;
3519       count = 0;
3520       PetscCall(PetscMalloc1(numBlocksPlus * vertsPerBlock, &vertToTrueVert));
3521       PetscCall(PetscMalloc1(numVertices * 3, &vtxCoords));
3522       for (PetscInt i = 0; i < numBlocksPlus * vertsPerBlock; i++) vertToTrueVert[i] = -1;
3523       for (PetscInt k = 0; k < extentPlus[2]; k++) {
3524         for (PetscInt j = 0; j < extentPlus[1]; j++) {
3525           for (PetscInt i = 0; i < extentPlus[0]; i++) {
3526             for (PetscInt v = 0; v < vertsPerBlock; v++) {
3527               PetscInt vIdx = ((k * extentPlus[1] + j) * extentPlus[0] + i) * vertsPerBlock + v;
3528 
3529               if (seen[vIdx]) {
3530                 PetscInt thisVert;
3531 
3532                 vertToTrueVert[vIdx] = thisVert = count++;
3533 
3534                 for (PetscInt d = 0; d < 3; d++) vtxCoords[3 * thisVert + d] = patternCoords[v][d];
3535                 vtxCoords[3 * thisVert + 0] += i * 2;
3536                 vtxCoords[3 * thisVert + 1] += j * 2;
3537                 vtxCoords[3 * thisVert + 2] += k * 2;
3538               }
3539             }
3540           }
3541         }
3542       }
3543       for (PetscInt i = 0; i < numBlocks; i++) {
3544         for (PetscInt f = 0; f < facesPerBlock; f++) {
3545           for (PetscInt v = 0; v < 4; v++) cells[i][f][v] = vertToTrueVert[cells[i][f][v]];
3546         }
3547       }
3548       PetscCall(PetscFree(vertToTrueVert));
3549       PetscCall(PetscFree(seen));
3550       cells_flat = cells[0][0];
3551       numEdges   = 0;
3552       for (PetscInt i = 0; i < numFaces; i++) {
3553         for (PetscInt e = 0; e < 4; e++) {
3554           PetscInt         ev[]       = {cells_flat[i * 4 + e], cells_flat[i * 4 + ((e + 1) % 4)]};
3555           const PetscReal *evCoords[] = {&vtxCoords[3 * ev[0]], &vtxCoords[3 * ev[1]]};
3556 
3557           for (PetscInt d = 0; d < 3; d++) {
3558             if (!periodic || periodic[0] != DM_BOUNDARY_PERIODIC) {
3559               if (evCoords[0][d] == 0. && evCoords[1][d] == 0.) numEdges++;
3560               if (evCoords[0][d] == 2. * extent[d] && evCoords[1][d] == 2. * extent[d]) numEdges++;
3561             }
3562           }
3563         }
3564       }
3565       PetscCall(PetscMalloc1(numEdges, &edges));
3566       PetscCall(PetscMalloc1(numEdges, &edgeSets));
3567       for (PetscInt edge = 0, i = 0; i < numFaces; i++) {
3568         for (PetscInt e = 0; e < 4; e++) {
3569           PetscInt         ev[]       = {cells_flat[i * 4 + e], cells_flat[i * 4 + ((e + 1) % 4)]};
3570           const PetscReal *evCoords[] = {&vtxCoords[3 * ev[0]], &vtxCoords[3 * ev[1]]};
3571 
3572           for (PetscInt d = 0; d < 3; d++) {
3573             if (!periodic || periodic[d] != DM_BOUNDARY_PERIODIC) {
3574               if (evCoords[0][d] == 0. && evCoords[1][d] == 0.) {
3575                 edges[edge][0]   = ev[0];
3576                 edges[edge][1]   = ev[1];
3577                 edgeSets[edge++] = 2 * d;
3578               }
3579               if (evCoords[0][d] == 2. * extent[d] && evCoords[1][d] == 2. * extent[d]) {
3580                 edges[edge][0]   = ev[0];
3581                 edges[edge][1]   = ev[1];
3582                 edgeSets[edge++] = 2 * d + 1;
3583               }
3584             }
3585           }
3586         }
3587       }
3588     }
3589     evalFunc   = TPSEvaluate_Gyroid;
3590     normalFunc = TPSExtrudeNormalFunc_Gyroid;
3591     break;
3592   }
3593 
3594   PetscCall(DMSetDimension(dm, topoDim));
3595   if (rank == 0) PetscCall(DMPlexBuildFromCellList(dm, numFaces, numVertices, 4, cells_flat));
3596   else PetscCall(DMPlexBuildFromCellList(dm, 0, 0, 0, NULL));
3597   PetscCall(PetscFree(cells_flat));
3598   {
3599     DM idm;
3600     PetscCall(DMPlexInterpolate(dm, &idm));
3601     PetscCall(DMPlexReplace_Internal(dm, &idm));
3602   }
3603   if (rank == 0) PetscCall(DMPlexBuildCoordinatesFromCellList(dm, spaceDim, vtxCoords));
3604   else PetscCall(DMPlexBuildCoordinatesFromCellList(dm, spaceDim, NULL));
3605   PetscCall(PetscFree(vtxCoords));
3606 
3607   PetscCall(DMCreateLabel(dm, "Face Sets"));
3608   PetscCall(DMGetLabel(dm, "Face Sets", &label));
3609   for (PetscInt e = 0; e < numEdges; e++) {
3610     PetscInt        njoin;
3611     const PetscInt *join, verts[] = {numFaces + edges[e][0], numFaces + edges[e][1]};
3612     PetscCall(DMPlexGetJoin(dm, 2, verts, &njoin, &join));
3613     PetscCheck(njoin == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Expected unique join of vertices %" PetscInt_FMT " and %" PetscInt_FMT, edges[e][0], edges[e][1]);
3614     PetscCall(DMLabelSetValue(label, join[0], edgeSets[e]));
3615     PetscCall(DMPlexRestoreJoin(dm, 2, verts, &njoin, &join));
3616   }
3617   PetscCall(PetscFree(edges));
3618   PetscCall(PetscFree(edgeSets));
3619   if (tps_distribute) {
3620     DM               pdm = NULL;
3621     PetscPartitioner part;
3622 
3623     PetscCall(DMPlexGetPartitioner(dm, &part));
3624     PetscCall(PetscPartitionerSetFromOptions(part));
3625     PetscCall(DMPlexDistribute(dm, 0, NULL, &pdm));
3626     if (pdm) PetscCall(DMPlexReplace_Internal(dm, &pdm));
3627     // Do not auto-distribute again
3628     PetscCall(DMPlexDistributeSetDefault(dm, PETSC_FALSE));
3629   }
3630 
3631   PetscCall(DMPlexSetRefinementUniform(dm, PETSC_TRUE));
3632   for (PetscInt refine = 0; refine < refinements; refine++) {
3633     PetscInt     m;
3634     DM           dmf;
3635     Vec          X;
3636     PetscScalar *x;
3637     PetscCall(DMRefine(dm, MPI_COMM_NULL, &dmf));
3638     PetscCall(DMPlexReplace_Internal(dm, &dmf));
3639 
3640     PetscCall(DMGetCoordinatesLocal(dm, &X));
3641     PetscCall(VecGetLocalSize(X, &m));
3642     PetscCall(VecGetArray(X, &x));
3643     for (PetscInt i = 0; i < m; i += 3) PetscCall(TPSNearestPoint(evalFunc, &x[i]));
3644     PetscCall(VecRestoreArray(X, &x));
3645   }
3646 
3647   // Face Sets has already been propagated to new vertices during refinement; this propagates to the initial vertices.
3648   PetscCall(DMGetLabel(dm, "Face Sets", &label));
3649   PetscCall(DMPlexLabelComplete(dm, label));
3650 
3651   PetscCall(PetscLogEventEnd(DMPLEX_Generate, dm, 0, 0, 0));
3652 
3653   if (thickness > 0) {
3654     DM              edm, cdm, ecdm;
3655     DMPlexTransform tr;
3656     const char     *prefix;
3657     PetscOptions    options;
3658     // Code from DMPlexExtrude
3659     PetscCall(DMPlexTransformCreate(PetscObjectComm((PetscObject)dm), &tr));
3660     PetscCall(DMPlexTransformSetDM(tr, dm));
3661     PetscCall(DMPlexTransformSetType(tr, DMPLEXEXTRUDE));
3662     PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
3663     PetscCall(PetscObjectSetOptionsPrefix((PetscObject)tr, prefix));
3664     PetscCall(PetscObjectGetOptions((PetscObject)dm, &options));
3665     PetscCall(PetscObjectSetOptions((PetscObject)tr, options));
3666     PetscCall(DMPlexTransformExtrudeSetLayers(tr, layers));
3667     PetscCall(DMPlexTransformExtrudeSetThickness(tr, thickness));
3668     PetscCall(DMPlexTransformExtrudeSetTensor(tr, PETSC_FALSE));
3669     PetscCall(DMPlexTransformExtrudeSetSymmetric(tr, PETSC_TRUE));
3670     PetscCall(DMPlexTransformExtrudeSetNormalFunction(tr, normalFunc));
3671     PetscCall(DMPlexTransformSetFromOptions(tr));
3672     PetscCall(PetscObjectSetOptions((PetscObject)tr, NULL));
3673     PetscCall(DMPlexTransformSetUp(tr));
3674     PetscCall(PetscObjectViewFromOptions((PetscObject)tr, NULL, "-dm_plex_tps_transform_view"));
3675     PetscCall(DMPlexTransformApply(tr, dm, &edm));
3676     PetscCall(DMCopyDisc(dm, edm));
3677     PetscCall(DMGetCoordinateDM(dm, &cdm));
3678     PetscCall(DMGetCoordinateDM(edm, &ecdm));
3679     PetscCall(DMCopyDisc(cdm, ecdm));
3680     PetscCall(DMPlexTransformCreateDiscLabels(tr, edm));
3681     PetscCall(DMPlexTransformDestroy(&tr));
3682     if (edm) {
3683       ((DM_Plex *)edm->data)->printFEM    = ((DM_Plex *)dm->data)->printFEM;
3684       ((DM_Plex *)edm->data)->printL2     = ((DM_Plex *)dm->data)->printL2;
3685       ((DM_Plex *)edm->data)->printLocate = ((DM_Plex *)dm->data)->printLocate;
3686     }
3687     PetscCall(DMPlexReplace_Internal(dm, &edm));
3688   }
3689   PetscFunctionReturn(PETSC_SUCCESS);
3690 }
3691 
3692 /*@
3693   DMPlexCreateTPSMesh - Create a distributed, interpolated mesh of a triply-periodic surface
3694 
3695   Collective
3696 
3697   Input Parameters:
3698 + comm           - The communicator for the `DM` object
3699 . tpstype        - Type of triply-periodic surface
3700 . extent         - Array of length 3 containing number of periods in each direction
3701 . periodic       - array of length 3 with periodicity, or `NULL` for non-periodic
3702 . tps_distribute - Distribute 2D manifold mesh prior to refinement and extrusion (more scalable)
3703 . refinements    - Number of factor-of-2 refinements of 2D manifold mesh
3704 . layers         - Number of cell layers extruded in normal direction
3705 - thickness      - Thickness in normal direction
3706 
3707   Output Parameter:
3708 . dm - The `DM` object
3709 
3710   Level: beginner
3711 
3712   Notes:
3713   This meshes the surface of the Schwarz P or Gyroid surfaces.  Schwarz P is the simplest member of the triply-periodic minimal surfaces.
3714   <https://en.wikipedia.org/wiki/Schwarz_minimal_surface#Schwarz_P_(%22Primitive%22)> and can be cut with "clean" boundaries.
3715   The Gyroid <https://en.wikipedia.org/wiki/Gyroid> is another triply-periodic minimal surface with applications in additive manufacturing; it is much more difficult to "cut" since there are no planes of symmetry.
3716   Our implementation creates a very coarse mesh of the surface and refines (by 4-way splitting) as many times as requested.
3717   On each refinement, all vertices are projected to their nearest point on the surface.
3718   This projection could readily be extended to related surfaces.
3719 
3720   See {cite}`maskery2018insights`
3721 
3722   The face (edge) sets for the Schwarz P surface are numbered $1(-x), 2(+x), 3(-y), 4(+y), 5(-z), 6(+z)$.
3723   When the mesh is refined, "Face Sets" contain the new vertices (created during refinement).
3724   Use `DMPlexLabelComplete()` to propagate to coarse-level vertices.
3725 
3726   Developer Notes:
3727   The Gyroid mesh does not currently mark boundary sets.
3728 
3729 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateSphereMesh()`, `DMSetType()`, `DMCreate()`
3730 @*/
3731 PetscErrorCode DMPlexCreateTPSMesh(MPI_Comm comm, DMPlexTPSType tpstype, const PetscInt extent[], const DMBoundaryType periodic[], PetscBool tps_distribute, PetscInt refinements, PetscInt layers, PetscReal thickness, DM *dm)
3732 {
3733   PetscFunctionBegin;
3734   PetscCall(DMCreate(comm, dm));
3735   PetscCall(DMSetType(*dm, DMPLEX));
3736   PetscCall(DMPlexCreateTPSMesh_Internal(*dm, tpstype, extent, periodic, tps_distribute, refinements, layers, thickness));
3737   PetscFunctionReturn(PETSC_SUCCESS);
3738 }
3739 
3740 /*@
3741   DMPlexCreateSphereMesh - Creates a mesh on the d-dimensional sphere, S^d.
3742 
3743   Collective
3744 
3745   Input Parameters:
3746 + comm    - The communicator for the `DM` object
3747 . dim     - The dimension
3748 . simplex - Use simplices, or tensor product cells
3749 - R       - The radius
3750 
3751   Output Parameter:
3752 . dm - The `DM` object
3753 
3754   Level: beginner
3755 
3756 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateBallMesh()`, `DMPlexCreateBoxMesh()`, `DMSetType()`, `DMCreate()`
3757 @*/
3758 PetscErrorCode DMPlexCreateSphereMesh(MPI_Comm comm, PetscInt dim, PetscBool simplex, PetscReal R, DM *dm)
3759 {
3760   PetscFunctionBegin;
3761   PetscAssertPointer(dm, 5);
3762   PetscCall(DMCreate(comm, dm));
3763   PetscCall(DMSetType(*dm, DMPLEX));
3764   PetscCall(DMPlexCreateSphereMesh_Internal(*dm, dim, simplex, R));
3765   PetscFunctionReturn(PETSC_SUCCESS);
3766 }
3767 
3768 static PetscErrorCode DMPlexCreateBallMesh_Internal(DM dm, PetscInt dim, PetscReal R)
3769 {
3770   DM          sdm, vol;
3771   DMLabel     bdlabel;
3772   const char *prefix;
3773 
3774   PetscFunctionBegin;
3775   PetscCall(DMCreate(PetscObjectComm((PetscObject)dm), &sdm));
3776   PetscCall(DMSetType(sdm, DMPLEX));
3777   PetscCall(DMGetOptionsPrefix(dm, &prefix));
3778   PetscCall(DMSetOptionsPrefix(sdm, prefix));
3779   PetscCall(DMAppendOptionsPrefix(sdm, "bd_"));
3780   PetscCall(DMPlexDistributeSetDefault(sdm, PETSC_FALSE));
3781   PetscCall(DMPlexCreateSphereMesh_Internal(sdm, dim - 1, PETSC_TRUE, R));
3782   PetscCall(DMSetFromOptions(sdm));
3783   PetscCall(DMViewFromOptions(sdm, NULL, "-dm_view"));
3784   PetscCall(DMPlexGenerate(sdm, NULL, PETSC_TRUE, &vol));
3785   PetscCall(DMDestroy(&sdm));
3786   PetscCall(DMPlexReplace_Internal(dm, &vol));
3787   PetscCall(DMCreateLabel(dm, "marker"));
3788   PetscCall(DMGetLabel(dm, "marker", &bdlabel));
3789   PetscCall(DMPlexMarkBoundaryFaces(dm, PETSC_DETERMINE, bdlabel));
3790   PetscCall(DMPlexLabelComplete(dm, bdlabel));
3791   PetscFunctionReturn(PETSC_SUCCESS);
3792 }
3793 
3794 /*@
3795   DMPlexCreateBallMesh - Creates a simplex mesh on the d-dimensional ball, B^d.
3796 
3797   Collective
3798 
3799   Input Parameters:
3800 + comm - The communicator for the `DM` object
3801 . dim  - The dimension
3802 - R    - The radius
3803 
3804   Output Parameter:
3805 . dm - The `DM` object
3806 
3807   Options Database Key:
3808 . bd_dm_refine - This will refine the surface mesh preserving the sphere geometry
3809 
3810   Level: beginner
3811 
3812 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateSphereMesh()`, `DMPlexCreateBoxMesh()`, `DMSetType()`, `DMCreate()`
3813 @*/
3814 PetscErrorCode DMPlexCreateBallMesh(MPI_Comm comm, PetscInt dim, PetscReal R, DM *dm)
3815 {
3816   PetscFunctionBegin;
3817   PetscCall(DMCreate(comm, dm));
3818   PetscCall(DMSetType(*dm, DMPLEX));
3819   PetscCall(DMPlexCreateBallMesh_Internal(*dm, dim, R));
3820   PetscFunctionReturn(PETSC_SUCCESS);
3821 }
3822 
3823 static PetscErrorCode DMPlexCreateReferenceCell_Internal(DM rdm, DMPolytopeType ct)
3824 {
3825   PetscFunctionBegin;
3826   switch (ct) {
3827   case DM_POLYTOPE_POINT: {
3828     PetscInt    numPoints[1]        = {1};
3829     PetscInt    coneSize[1]         = {0};
3830     PetscInt    cones[1]            = {0};
3831     PetscInt    coneOrientations[1] = {0};
3832     PetscScalar vertexCoords[1]     = {0.0};
3833 
3834     PetscCall(DMSetDimension(rdm, 0));
3835     PetscCall(DMPlexCreateFromDAG(rdm, 0, numPoints, coneSize, cones, coneOrientations, vertexCoords));
3836   } break;
3837   case DM_POLYTOPE_SEGMENT: {
3838     PetscInt    numPoints[2]        = {2, 1};
3839     PetscInt    coneSize[3]         = {2, 0, 0};
3840     PetscInt    cones[2]            = {1, 2};
3841     PetscInt    coneOrientations[2] = {0, 0};
3842     PetscScalar vertexCoords[2]     = {-1.0, 1.0};
3843 
3844     PetscCall(DMSetDimension(rdm, 1));
3845     PetscCall(DMPlexCreateFromDAG(rdm, 1, numPoints, coneSize, cones, coneOrientations, vertexCoords));
3846   } break;
3847   case DM_POLYTOPE_POINT_PRISM_TENSOR: {
3848     PetscInt    numPoints[2]        = {2, 1};
3849     PetscInt    coneSize[3]         = {2, 0, 0};
3850     PetscInt    cones[2]            = {1, 2};
3851     PetscInt    coneOrientations[2] = {0, 0};
3852     PetscScalar vertexCoords[2]     = {-1.0, 1.0};
3853 
3854     PetscCall(DMSetDimension(rdm, 1));
3855     PetscCall(DMPlexCreateFromDAG(rdm, 1, numPoints, coneSize, cones, coneOrientations, vertexCoords));
3856   } break;
3857   case DM_POLYTOPE_TRIANGLE: {
3858     PetscInt    numPoints[2]        = {3, 1};
3859     PetscInt    coneSize[4]         = {3, 0, 0, 0};
3860     PetscInt    cones[3]            = {1, 2, 3};
3861     PetscInt    coneOrientations[3] = {0, 0, 0};
3862     PetscScalar vertexCoords[6]     = {-1.0, -1.0, 1.0, -1.0, -1.0, 1.0};
3863 
3864     PetscCall(DMSetDimension(rdm, 2));
3865     PetscCall(DMPlexCreateFromDAG(rdm, 1, numPoints, coneSize, cones, coneOrientations, vertexCoords));
3866   } break;
3867   case DM_POLYTOPE_QUADRILATERAL: {
3868     PetscInt    numPoints[2]        = {4, 1};
3869     PetscInt    coneSize[5]         = {4, 0, 0, 0, 0};
3870     PetscInt    cones[4]            = {1, 2, 3, 4};
3871     PetscInt    coneOrientations[4] = {0, 0, 0, 0};
3872     PetscScalar vertexCoords[8]     = {-1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0};
3873 
3874     PetscCall(DMSetDimension(rdm, 2));
3875     PetscCall(DMPlexCreateFromDAG(rdm, 1, numPoints, coneSize, cones, coneOrientations, vertexCoords));
3876   } break;
3877   case DM_POLYTOPE_SEG_PRISM_TENSOR: {
3878     PetscInt    numPoints[2]        = {4, 1};
3879     PetscInt    coneSize[5]         = {4, 0, 0, 0, 0};
3880     PetscInt    cones[4]            = {1, 2, 3, 4};
3881     PetscInt    coneOrientations[4] = {0, 0, 0, 0};
3882     PetscScalar vertexCoords[8]     = {-1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0};
3883 
3884     PetscCall(DMSetDimension(rdm, 2));
3885     PetscCall(DMPlexCreateFromDAG(rdm, 1, numPoints, coneSize, cones, coneOrientations, vertexCoords));
3886   } break;
3887   case DM_POLYTOPE_TETRAHEDRON: {
3888     PetscInt    numPoints[2]        = {4, 1};
3889     PetscInt    coneSize[5]         = {4, 0, 0, 0, 0};
3890     PetscInt    cones[4]            = {1, 2, 3, 4};
3891     PetscInt    coneOrientations[4] = {0, 0, 0, 0};
3892     PetscScalar vertexCoords[12]    = {-1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, 1.0};
3893 
3894     PetscCall(DMSetDimension(rdm, 3));
3895     PetscCall(DMPlexCreateFromDAG(rdm, 1, numPoints, coneSize, cones, coneOrientations, vertexCoords));
3896   } break;
3897   case DM_POLYTOPE_HEXAHEDRON: {
3898     PetscInt    numPoints[2]        = {8, 1};
3899     PetscInt    coneSize[9]         = {8, 0, 0, 0, 0, 0, 0, 0, 0};
3900     PetscInt    cones[8]            = {1, 2, 3, 4, 5, 6, 7, 8};
3901     PetscInt    coneOrientations[8] = {0, 0, 0, 0, 0, 0, 0, 0};
3902     PetscScalar vertexCoords[24]    = {-1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0};
3903 
3904     PetscCall(DMSetDimension(rdm, 3));
3905     PetscCall(DMPlexCreateFromDAG(rdm, 1, numPoints, coneSize, cones, coneOrientations, vertexCoords));
3906   } break;
3907   case DM_POLYTOPE_TRI_PRISM: {
3908     PetscInt    numPoints[2]        = {6, 1};
3909     PetscInt    coneSize[7]         = {6, 0, 0, 0, 0, 0, 0};
3910     PetscInt    cones[6]            = {1, 2, 3, 4, 5, 6};
3911     PetscInt    coneOrientations[6] = {0, 0, 0, 0, 0, 0};
3912     PetscScalar vertexCoords[18]    = {-1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, 1.0, 1.0};
3913 
3914     PetscCall(DMSetDimension(rdm, 3));
3915     PetscCall(DMPlexCreateFromDAG(rdm, 1, numPoints, coneSize, cones, coneOrientations, vertexCoords));
3916   } break;
3917   case DM_POLYTOPE_TRI_PRISM_TENSOR: {
3918     PetscInt    numPoints[2]        = {6, 1};
3919     PetscInt    coneSize[7]         = {6, 0, 0, 0, 0, 0, 0};
3920     PetscInt    cones[6]            = {1, 2, 3, 4, 5, 6};
3921     PetscInt    coneOrientations[6] = {0, 0, 0, 0, 0, 0};
3922     PetscScalar vertexCoords[18]    = {-1.0, -1.0, -1.0, 1.0, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, 1.0, 1.0};
3923 
3924     PetscCall(DMSetDimension(rdm, 3));
3925     PetscCall(DMPlexCreateFromDAG(rdm, 1, numPoints, coneSize, cones, coneOrientations, vertexCoords));
3926   } break;
3927   case DM_POLYTOPE_QUAD_PRISM_TENSOR: {
3928     PetscInt    numPoints[2]        = {8, 1};
3929     PetscInt    coneSize[9]         = {8, 0, 0, 0, 0, 0, 0, 0, 0};
3930     PetscInt    cones[8]            = {1, 2, 3, 4, 5, 6, 7, 8};
3931     PetscInt    coneOrientations[8] = {0, 0, 0, 0, 0, 0, 0, 0};
3932     PetscScalar vertexCoords[24]    = {-1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0};
3933 
3934     PetscCall(DMSetDimension(rdm, 3));
3935     PetscCall(DMPlexCreateFromDAG(rdm, 1, numPoints, coneSize, cones, coneOrientations, vertexCoords));
3936   } break;
3937   case DM_POLYTOPE_PYRAMID: {
3938     PetscInt    numPoints[2]        = {5, 1};
3939     PetscInt    coneSize[6]         = {5, 0, 0, 0, 0, 0};
3940     PetscInt    cones[5]            = {1, 2, 3, 4, 5};
3941     PetscInt    coneOrientations[8] = {0, 0, 0, 0, 0, 0, 0, 0};
3942     PetscScalar vertexCoords[24]    = {-1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0, 0.0, 0.0, 1.0};
3943 
3944     PetscCall(DMSetDimension(rdm, 3));
3945     PetscCall(DMPlexCreateFromDAG(rdm, 1, numPoints, coneSize, cones, coneOrientations, vertexCoords));
3946   } break;
3947   default:
3948     SETERRQ(PetscObjectComm((PetscObject)rdm), PETSC_ERR_ARG_WRONG, "Cannot create reference cell for cell type %s", DMPolytopeTypes[ct]);
3949   }
3950   {
3951     PetscInt Nv, v;
3952 
3953     /* Must create the celltype label here so that we do not automatically try to compute the types */
3954     PetscCall(DMCreateLabel(rdm, "celltype"));
3955     PetscCall(DMPlexSetCellType(rdm, 0, ct));
3956     PetscCall(DMPlexGetChart(rdm, NULL, &Nv));
3957     for (v = 1; v < Nv; ++v) PetscCall(DMPlexSetCellType(rdm, v, DM_POLYTOPE_POINT));
3958   }
3959   PetscCall(DMPlexInterpolateInPlace_Internal(rdm));
3960   PetscCall(PetscObjectSetName((PetscObject)rdm, DMPolytopeTypes[ct]));
3961   PetscFunctionReturn(PETSC_SUCCESS);
3962 }
3963 
3964 /*@
3965   DMPlexCreateReferenceCell - Create a `DMPLEX` with the appropriate FEM reference cell
3966 
3967   Collective
3968 
3969   Input Parameters:
3970 + comm - The communicator
3971 - ct   - The cell type of the reference cell
3972 
3973   Output Parameter:
3974 . refdm - The reference cell
3975 
3976   Level: intermediate
3977 
3978 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateBoxMesh()`
3979 @*/
3980 PetscErrorCode DMPlexCreateReferenceCell(MPI_Comm comm, DMPolytopeType ct, DM *refdm)
3981 {
3982   PetscFunctionBegin;
3983   PetscCall(DMCreate(comm, refdm));
3984   PetscCall(DMSetType(*refdm, DMPLEX));
3985   PetscCall(DMPlexCreateReferenceCell_Internal(*refdm, ct));
3986   PetscFunctionReturn(PETSC_SUCCESS);
3987 }
3988 
3989 static PetscErrorCode DMPlexCreateBoundaryLabel_Private(DM dm, const char name[])
3990 {
3991   DM        plex;
3992   DMLabel   label;
3993   PetscBool hasLabel;
3994 
3995   PetscFunctionBegin;
3996   PetscCall(DMHasLabel(dm, name, &hasLabel));
3997   if (hasLabel) PetscFunctionReturn(PETSC_SUCCESS);
3998   PetscCall(DMCreateLabel(dm, name));
3999   PetscCall(DMGetLabel(dm, name, &label));
4000   PetscCall(DMConvert(dm, DMPLEX, &plex));
4001   PetscCall(DMPlexMarkBoundaryFaces(plex, 1, label));
4002   PetscCall(DMPlexLabelComplete(plex, label));
4003   PetscCall(DMDestroy(&plex));
4004   PetscFunctionReturn(PETSC_SUCCESS);
4005 }
4006 
4007 /*
4008   We use the last coordinate as the radius, the inner radius is lower[dim-1] and the outer radius is upper[dim-1]. Then we map the first coordinate around the circle.
4009 
4010     (x, y) -> (r, theta) = (x[1], (x[0] - lower[0]) * 2\pi/(upper[0] - lower[0]))
4011 */
4012 static void boxToAnnulus(PetscInt dim, PetscInt Nf, PetscInt NfAux, const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[], const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[], PetscReal t, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar f0[])
4013 {
4014   const PetscReal low = PetscRealPart(constants[0]);
4015   const PetscReal upp = PetscRealPart(constants[1]);
4016   const PetscReal r   = PetscRealPart(u[1]);
4017   const PetscReal th  = 2. * PETSC_PI * (PetscRealPart(u[0]) - low) / (upp - low);
4018 
4019   f0[0] = r * PetscCosReal(th);
4020   f0[1] = r * PetscSinReal(th);
4021 }
4022 
4023 // Insert vertices and their joins, marked by depth
4024 static PetscErrorCode ProcessCohesiveLabel_Vertices(DM dm, DMLabel label, DMLabel vlabel, PetscInt val, PetscInt n, const PetscInt vertices[])
4025 {
4026   PetscFunctionBegin;
4027   PetscCall(DMPlexMarkSubmesh_Interpolated(dm, vlabel, val, PETSC_FALSE, PETSC_FALSE, label, NULL));
4028   PetscFunctionReturn(PETSC_SUCCESS);
4029 }
4030 
4031 // Insert faces and their closures, marked by depth
4032 static PetscErrorCode ProcessCohesiveLabel_Faces(DM dm, DMLabel label, PetscInt n, const PetscInt faces[])
4033 {
4034   PetscFunctionBegin;
4035   for (PetscInt p = 0; p < n; ++p) {
4036     const PetscInt point   = faces[p];
4037     PetscInt      *closure = NULL;
4038     PetscInt       clSize, pdepth;
4039 
4040     PetscCall(DMPlexGetPointDepth(dm, point, &pdepth));
4041     PetscCall(DMLabelSetValue(label, point, pdepth));
4042     PetscCall(DMPlexGetTransitiveClosure(dm, point, PETSC_TRUE, &clSize, &closure));
4043     for (PetscInt cl = 0; cl < clSize * 2; cl += 2) {
4044       PetscCall(DMPlexGetPointDepth(dm, closure[cl], &pdepth));
4045       PetscCall(DMLabelSetValue(label, closure[cl], pdepth));
4046     }
4047     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, &clSize, &closure));
4048   }
4049   PetscFunctionReturn(PETSC_SUCCESS);
4050 }
4051 
4052 PETSC_EXTERN PetscErrorCode PetscOptionsFindPairPrefix_Private(PetscOptions, const char pre[], const char name[], const char *option[], const char *value[], PetscBool *flg);
4053 
4054 const char *const DMPlexShapes[] = {"box", "box_surface", "ball", "sphere", "cylinder", "schwarz_p", "gyroid", "doublet", "annulus", "hypercubic", "zbox", "unknown", "DMPlexShape", "DM_SHAPE_", NULL};
4055 
4056 static PetscErrorCode DMPlexCreateFromOptions_Internal(PetscOptionItems *PetscOptionsObject, PetscBool *useCoordSpace, DM dm)
4057 {
4058   DMPlexShape    shape   = DM_SHAPE_BOX;
4059   DMPolytopeType cell    = DM_POLYTOPE_TRIANGLE;
4060   PetscInt       dim     = 2;
4061   PetscBool      simplex = PETSC_TRUE, interpolate = PETSC_TRUE, adjCone = PETSC_FALSE, adjClosure = PETSC_TRUE, refDomain = PETSC_FALSE;
4062   PetscBool      flg, flg2, fflg, strflg, bdfflg, nameflg;
4063   MPI_Comm       comm;
4064   char           filename[PETSC_MAX_PATH_LEN]   = "<unspecified>";
4065   char           bdFilename[PETSC_MAX_PATH_LEN] = "<unspecified>";
4066   char           plexname[PETSC_MAX_PATH_LEN]   = "";
4067   const char    *option;
4068 
4069   PetscFunctionBegin;
4070   PetscCall(PetscLogEventBegin(DMPLEX_CreateFromOptions, dm, 0, 0, 0));
4071   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
4072   /* TODO Turn this into a registration interface */
4073   PetscCall(PetscOptionsString("-dm_plex_filename", "File containing a mesh", "DMPlexCreateFromFile", filename, filename, sizeof(filename), &fflg));
4074   PetscCall(PetscOptionsString("-dm_plex_file_contents", "Contents of a file format in a string", "DMPlexCreateFromFile", filename, filename, sizeof(filename), &strflg));
4075   PetscCall(PetscOptionsString("-dm_plex_boundary_filename", "File containing a mesh boundary", "DMPlexCreateFromFile", bdFilename, bdFilename, sizeof(bdFilename), &bdfflg));
4076   PetscCall(PetscOptionsString("-dm_plex_name", "Name of the mesh in the file", "DMPlexCreateFromFile", plexname, plexname, sizeof(plexname), &nameflg));
4077   PetscCall(PetscOptionsEnum("-dm_plex_cell", "Cell shape", "", DMPolytopeTypes, (PetscEnum)cell, (PetscEnum *)&cell, NULL));
4078   PetscCall(PetscOptionsBool("-dm_plex_reference_cell_domain", "Use a reference cell domain", "", refDomain, &refDomain, NULL));
4079   PetscCall(PetscOptionsEnum("-dm_plex_shape", "Shape for built-in mesh", "", DMPlexShapes, (PetscEnum)shape, (PetscEnum *)&shape, &flg));
4080   PetscCall(PetscOptionsBoundedInt("-dm_plex_dim", "Topological dimension of the mesh", "DMGetDimension", dim, &dim, &flg, 0));
4081   PetscCall(PetscOptionsBool("-dm_plex_simplex", "Mesh cell shape", "", simplex, &simplex, &flg));
4082   PetscCall(PetscOptionsBool("-dm_plex_interpolate", "Flag to create edges and faces automatically", "", interpolate, &interpolate, &flg));
4083   PetscCall(PetscOptionsBool("-dm_plex_adj_cone", "Set adjacency direction", "DMSetBasicAdjacency", adjCone, &adjCone, &flg));
4084   PetscCall(PetscOptionsBool("-dm_plex_adj_closure", "Set adjacency size", "DMSetBasicAdjacency", adjClosure, &adjClosure, &flg2));
4085   if (flg || flg2) PetscCall(DMSetBasicAdjacency(dm, adjCone, adjClosure));
4086 
4087   switch (cell) {
4088   case DM_POLYTOPE_POINT:
4089   case DM_POLYTOPE_SEGMENT:
4090   case DM_POLYTOPE_POINT_PRISM_TENSOR:
4091   case DM_POLYTOPE_TRIANGLE:
4092   case DM_POLYTOPE_QUADRILATERAL:
4093   case DM_POLYTOPE_TETRAHEDRON:
4094   case DM_POLYTOPE_HEXAHEDRON:
4095     *useCoordSpace = PETSC_TRUE;
4096     break;
4097   default:
4098     *useCoordSpace = PETSC_FALSE;
4099     break;
4100   }
4101 
4102   if (fflg) {
4103     DM          dmnew;
4104     const char *name;
4105 
4106     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
4107     PetscCall(DMPlexCreateFromFile(PetscObjectComm((PetscObject)dm), filename, nameflg ? plexname : name, interpolate, &dmnew));
4108     PetscCall(DMPlexCopy_Internal(dm, PETSC_FALSE, PETSC_FALSE, dmnew));
4109     PetscCall(DMPlexReplace_Internal(dm, &dmnew));
4110   } else if (refDomain) {
4111     PetscCall(DMPlexCreateReferenceCell_Internal(dm, cell));
4112   } else if (bdfflg) {
4113     DM          bdm, dmnew;
4114     const char *name;
4115 
4116     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
4117     PetscCall(DMPlexCreateFromFile(PetscObjectComm((PetscObject)dm), bdFilename, nameflg ? plexname : name, interpolate, &bdm));
4118     PetscCall(PetscObjectSetOptionsPrefix((PetscObject)bdm, "bd_"));
4119     PetscCall(DMSetFromOptions(bdm));
4120     PetscCall(DMPlexGenerate(bdm, NULL, interpolate, &dmnew));
4121     PetscCall(DMDestroy(&bdm));
4122     PetscCall(DMPlexCopy_Internal(dm, PETSC_FALSE, PETSC_FALSE, dmnew));
4123     PetscCall(DMPlexReplace_Internal(dm, &dmnew));
4124   } else if (strflg) {
4125     DM          dmnew;
4126     PetscViewer viewer;
4127     const char *contents;
4128     char       *strname;
4129     char        tmpdir[PETSC_MAX_PATH_LEN];
4130     char        tmpfilename[PETSC_MAX_PATH_LEN];
4131     char        name[PETSC_MAX_PATH_LEN];
4132     MPI_Comm    comm;
4133     PetscMPIInt rank;
4134 
4135     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
4136     PetscCallMPI(MPI_Comm_rank(comm, &rank));
4137     PetscCall(PetscStrchr(filename, ':', &strname));
4138     PetscCheck(strname, comm, PETSC_ERR_ARG_WRONG, "File contents must have the form \"ext:string_name\", not %s", filename);
4139     strname[0] = '\0';
4140     ++strname;
4141     PetscCall(PetscDLSym(NULL, strname, (void **)&contents));
4142     PetscCheck(contents, comm, PETSC_ERR_ARG_WRONG, "Could not locate mesh string %s", strname);
4143     PetscCall(PetscGetTmp(comm, tmpdir, PETSC_MAX_PATH_LEN));
4144     PetscCall(PetscStrlcat(tmpdir, "/meshXXXXXX", PETSC_MAX_PATH_LEN));
4145     PetscCall(PetscMkdtemp(tmpdir));
4146     PetscCall(PetscSNPrintf(tmpfilename, PETSC_MAX_PATH_LEN, "%s/mesh.%s", tmpdir, filename));
4147     PetscCall(PetscViewerASCIIOpen(comm, tmpfilename, &viewer));
4148     PetscCall(PetscViewerASCIIPrintf(viewer, "%s\n", contents));
4149     PetscCall(PetscViewerDestroy(&viewer));
4150     PetscCall(DMPlexCreateFromFile(PetscObjectComm((PetscObject)dm), tmpfilename, plexname, interpolate, &dmnew));
4151     PetscCall(PetscRMTree(tmpdir));
4152     PetscCall(PetscSNPrintf(name, PETSC_MAX_PATH_LEN, "%s Mesh", strname));
4153     PetscCall(PetscObjectSetName((PetscObject)dm, name));
4154     PetscCall(DMPlexCopy_Internal(dm, PETSC_FALSE, PETSC_FALSE, dmnew));
4155     PetscCall(DMPlexReplace_Internal(dm, &dmnew));
4156   } else {
4157     PetscCall(PetscObjectSetName((PetscObject)dm, DMPlexShapes[shape]));
4158     switch (shape) {
4159     case DM_SHAPE_BOX:
4160     case DM_SHAPE_ZBOX:
4161     case DM_SHAPE_ANNULUS: {
4162       PetscInt       faces[3]  = {0, 0, 0};
4163       PetscReal      lower[3]  = {0, 0, 0};
4164       PetscReal      upper[3]  = {1, 1, 1};
4165       DMBoundaryType bdt[3]    = {DM_BOUNDARY_NONE, DM_BOUNDARY_NONE, DM_BOUNDARY_NONE};
4166       PetscBool      isAnnular = shape == DM_SHAPE_ANNULUS ? PETSC_TRUE : PETSC_FALSE;
4167       PetscInt       i, n;
4168 
4169       n = dim;
4170       for (i = 0; i < dim; ++i) faces[i] = (dim == 1 ? 1 : 4 - dim);
4171       PetscCall(PetscOptionsIntArray("-dm_plex_box_faces", "Number of faces along each dimension", "", faces, &n, &flg));
4172       n = 3;
4173       PetscCall(PetscOptionsRealArray("-dm_plex_box_lower", "Lower left corner of box", "", lower, &n, &flg));
4174       PetscCheck(!flg || !(n != dim), comm, PETSC_ERR_ARG_SIZ, "Lower box point had %" PetscInt_FMT " values, should have been %" PetscInt_FMT, n, dim);
4175       n = 3;
4176       PetscCall(PetscOptionsRealArray("-dm_plex_box_upper", "Upper right corner of box", "", upper, &n, &flg));
4177       PetscCheck(!flg || !(n != dim), comm, PETSC_ERR_ARG_SIZ, "Upper box point had %" PetscInt_FMT " values, should have been %" PetscInt_FMT, n, dim);
4178       n = 3;
4179       PetscCall(PetscOptionsEnumArray("-dm_plex_box_bd", "Boundary type for each dimension", "", DMBoundaryTypes, (PetscEnum *)bdt, &n, &flg));
4180       PetscCheck(!flg || !(n != dim), comm, PETSC_ERR_ARG_SIZ, "Box boundary types had %" PetscInt_FMT " values, should have been %" PetscInt_FMT, n, dim);
4181 
4182       PetscCheck(!isAnnular || dim == 2, comm, PETSC_ERR_ARG_OUTOFRANGE, "Only two dimensional annuli have been implemented");
4183       if (isAnnular)
4184         for (i = 0; i < dim - 1; ++i) bdt[i] = DM_BOUNDARY_PERIODIC;
4185 
4186       switch (cell) {
4187       case DM_POLYTOPE_TRI_PRISM_TENSOR:
4188         PetscCall(DMPlexCreateWedgeBoxMesh_Internal(dm, faces, lower, upper, bdt));
4189         if (!interpolate) {
4190           DM udm;
4191 
4192           PetscCall(DMPlexUninterpolate(dm, &udm));
4193           PetscCall(DMPlexReplace_Internal(dm, &udm));
4194         }
4195         break;
4196       default:
4197         PetscCall(DMPlexCreateBoxMesh_Internal(dm, shape, dim, simplex, faces, lower, upper, bdt, interpolate));
4198         break;
4199       }
4200       if (isAnnular) {
4201         DM          cdm;
4202         PetscDS     cds;
4203         PetscScalar bounds[2] = {lower[0], upper[0]};
4204 
4205         // Fix coordinates for annular region
4206         PetscCall(DMSetPeriodicity(dm, NULL, NULL, NULL));
4207         PetscCall(DMSetCellCoordinatesLocal(dm, NULL));
4208         PetscCall(DMSetCellCoordinates(dm, NULL));
4209         PetscCall(DMPlexCreateCoordinateSpace(dm, 1, PETSC_TRUE, NULL));
4210         PetscCall(DMGetCoordinateDM(dm, &cdm));
4211         PetscCall(DMGetDS(cdm, &cds));
4212         PetscCall(PetscDSSetConstants(cds, 2, bounds));
4213         PetscCall(DMPlexRemapGeometry(dm, 0.0, boxToAnnulus));
4214       }
4215     } break;
4216     case DM_SHAPE_BOX_SURFACE: {
4217       PetscInt  faces[3] = {0, 0, 0};
4218       PetscReal lower[3] = {0, 0, 0};
4219       PetscReal upper[3] = {1, 1, 1};
4220       PetscInt  i, n;
4221 
4222       n = dim + 1;
4223       for (i = 0; i < dim + 1; ++i) faces[i] = (dim + 1 == 1 ? 1 : 4 - (dim + 1));
4224       PetscCall(PetscOptionsIntArray("-dm_plex_box_faces", "Number of faces along each dimension", "", faces, &n, &flg));
4225       n = 3;
4226       PetscCall(PetscOptionsRealArray("-dm_plex_box_lower", "Lower left corner of box", "", lower, &n, &flg));
4227       PetscCheck(!flg || !(n != dim + 1), comm, PETSC_ERR_ARG_SIZ, "Lower box point had %" PetscInt_FMT " values, should have been %" PetscInt_FMT, n, dim + 1);
4228       n = 3;
4229       PetscCall(PetscOptionsRealArray("-dm_plex_box_upper", "Upper right corner of box", "", upper, &n, &flg));
4230       PetscCheck(!flg || !(n != dim + 1), comm, PETSC_ERR_ARG_SIZ, "Upper box point had %" PetscInt_FMT " values, should have been %" PetscInt_FMT, n, dim + 1);
4231       PetscCall(DMPlexCreateBoxSurfaceMesh_Internal(dm, dim + 1, faces, lower, upper, interpolate));
4232     } break;
4233     case DM_SHAPE_SPHERE: {
4234       PetscReal R = 1.0;
4235 
4236       PetscCall(PetscOptionsReal("-dm_plex_sphere_radius", "Radius of the sphere", "", R, &R, &flg));
4237       PetscCall(DMPlexCreateSphereMesh_Internal(dm, dim, simplex, R));
4238     } break;
4239     case DM_SHAPE_BALL: {
4240       PetscReal R = 1.0;
4241 
4242       PetscCall(PetscOptionsReal("-dm_plex_ball_radius", "Radius of the ball", "", R, &R, &flg));
4243       PetscCall(DMPlexCreateBallMesh_Internal(dm, dim, R));
4244     } break;
4245     case DM_SHAPE_CYLINDER: {
4246       DMBoundaryType bdt = DM_BOUNDARY_NONE;
4247       PetscInt       Nw  = 6;
4248       PetscInt       Nr  = 0;
4249 
4250       PetscCall(PetscOptionsEnum("-dm_plex_cylinder_bd", "Boundary type in the z direction", "", DMBoundaryTypes, (PetscEnum)bdt, (PetscEnum *)&bdt, NULL));
4251       PetscCall(PetscOptionsInt("-dm_plex_cylinder_num_wedges", "Number of wedges around the cylinder", "", Nw, &Nw, NULL));
4252       PetscCall(PetscOptionsInt("-dm_plex_cylinder_num_refine", "Number of refinements before projection", "", Nr, &Nr, NULL));
4253       switch (cell) {
4254       case DM_POLYTOPE_TRI_PRISM_TENSOR:
4255         PetscCall(DMPlexCreateWedgeCylinderMesh_Internal(dm, Nw, interpolate));
4256         break;
4257       default:
4258         PetscCall(DMPlexCreateHexCylinderMesh_Internal(dm, bdt, Nr));
4259         break;
4260       }
4261     } break;
4262     case DM_SHAPE_SCHWARZ_P: // fallthrough
4263     case DM_SHAPE_GYROID: {
4264       PetscInt       extent[3] = {1, 1, 1}, refine = 0, layers = 0, three;
4265       PetscReal      thickness   = 0.;
4266       DMBoundaryType periodic[3] = {DM_BOUNDARY_NONE, DM_BOUNDARY_NONE, DM_BOUNDARY_NONE};
4267       DMPlexTPSType  tps_type    = shape == DM_SHAPE_SCHWARZ_P ? DMPLEX_TPS_SCHWARZ_P : DMPLEX_TPS_GYROID;
4268       PetscBool      tps_distribute;
4269       PetscCall(PetscOptionsIntArray("-dm_plex_tps_extent", "Number of replicas for each of three dimensions", NULL, extent, (three = 3, &three), NULL));
4270       PetscCall(PetscOptionsInt("-dm_plex_tps_refine", "Number of refinements", NULL, refine, &refine, NULL));
4271       PetscCall(PetscOptionsEnumArray("-dm_plex_tps_periodic", "Periodicity in each of three dimensions", NULL, DMBoundaryTypes, (PetscEnum *)periodic, (three = 3, &three), NULL));
4272       PetscCall(PetscOptionsInt("-dm_plex_tps_layers", "Number of layers in volumetric extrusion (or zero to not extrude)", NULL, layers, &layers, NULL));
4273       PetscCall(PetscOptionsReal("-dm_plex_tps_thickness", "Thickness of volumetric extrusion", NULL, thickness, &thickness, NULL));
4274       PetscCall(DMPlexDistributeGetDefault(dm, &tps_distribute));
4275       PetscCall(PetscOptionsBool("-dm_plex_tps_distribute", "Distribute the 2D mesh prior to refinement and extrusion", NULL, tps_distribute, &tps_distribute, NULL));
4276       PetscCall(DMPlexCreateTPSMesh_Internal(dm, tps_type, extent, periodic, tps_distribute, refine, layers, thickness));
4277     } break;
4278     case DM_SHAPE_DOUBLET: {
4279       DM        dmnew;
4280       PetscReal rl = 0.0;
4281 
4282       PetscCall(PetscOptionsReal("-dm_plex_doublet_refinementlimit", "Refinement limit", NULL, rl, &rl, NULL));
4283       PetscCall(DMPlexCreateDoublet(PetscObjectComm((PetscObject)dm), dim, simplex, interpolate, rl, &dmnew));
4284       PetscCall(DMPlexCopy_Internal(dm, PETSC_FALSE, PETSC_FALSE, dmnew));
4285       PetscCall(DMPlexReplace_Internal(dm, &dmnew));
4286     } break;
4287     case DM_SHAPE_HYPERCUBIC: {
4288       PetscInt       *edges;
4289       PetscReal      *lower, *upper;
4290       DMBoundaryType *bdt;
4291       PetscInt        n, d;
4292 
4293       *useCoordSpace = PETSC_FALSE;
4294       PetscCall(PetscMalloc4(dim, &edges, dim, &lower, dim, &upper, dim, &bdt));
4295       for (d = 0; d < dim; ++d) {
4296         edges[d] = 1;
4297         lower[d] = 0.;
4298         upper[d] = 1.;
4299         bdt[d]   = DM_BOUNDARY_PERIODIC;
4300       }
4301       n = dim;
4302       PetscCall(PetscOptionsIntArray("-dm_plex_box_faces", "Number of faces along each dimension", "", edges, &n, &flg));
4303       n = dim;
4304       PetscCall(PetscOptionsRealArray("-dm_plex_box_lower", "Lower left corner of box", "", lower, &n, &flg));
4305       PetscCheck(!flg || n == dim, comm, PETSC_ERR_ARG_SIZ, "Lower box point had %" PetscInt_FMT " values, should have been %" PetscInt_FMT, n, dim);
4306       n = dim;
4307       PetscCall(PetscOptionsRealArray("-dm_plex_box_upper", "Upper right corner of box", "", upper, &n, &flg));
4308       PetscCheck(!flg || n == dim, comm, PETSC_ERR_ARG_SIZ, "Upper box point had %" PetscInt_FMT " values, should have been %" PetscInt_FMT, n, dim);
4309       n = dim;
4310       PetscCall(PetscOptionsEnumArray("-dm_plex_box_bd", "Boundary type for each dimension", "", DMBoundaryTypes, (PetscEnum *)bdt, &n, &flg));
4311       PetscCheck(!flg || n == dim, comm, PETSC_ERR_ARG_SIZ, "Box boundary types had %" PetscInt_FMT " values, should have been %" PetscInt_FMT, n, dim);
4312       PetscCall(DMPlexCreateHypercubicMesh_Internal(dm, dim, lower, upper, edges, bdt));
4313       PetscCall(PetscFree4(edges, lower, upper, bdt));
4314     } break;
4315     default:
4316       SETERRQ(comm, PETSC_ERR_SUP, "Domain shape %s is unsupported", DMPlexShapes[shape]);
4317     }
4318   }
4319   PetscCall(DMPlexSetRefinementUniform(dm, PETSC_TRUE));
4320   if (!((PetscObject)dm)->name && nameflg) PetscCall(PetscObjectSetName((PetscObject)dm, plexname));
4321   // Allow label creation
4322   PetscCall(PetscOptionsFindPairPrefix_Private(NULL, ((PetscObject)dm)->prefix, "-dm_plex_label_", &option, NULL, &flg));
4323   if (flg) {
4324     DMLabel     label;
4325     PetscInt    points[1024], n = 1024;
4326     char        fulloption[PETSC_MAX_PATH_LEN];
4327     const char *name = &option[14];
4328 
4329     PetscCall(DMCreateLabel(dm, name));
4330     PetscCall(DMGetLabel(dm, name, &label));
4331     fulloption[0] = '-';
4332     fulloption[1] = 0;
4333     PetscCall(PetscStrlcat(fulloption, option, PETSC_MAX_PATH_LEN));
4334     PetscCall(PetscOptionsGetIntArray(NULL, ((PetscObject)dm)->prefix, fulloption, points, &n, NULL));
4335     for (PetscInt p = 0; p < n; ++p) PetscCall(DMLabelSetValue(label, points[p], 1));
4336   }
4337   // Allow cohesive label creation
4338   //   Faces are input, completed, and all points are marked with their depth
4339   PetscCall(PetscOptionsFindPairPrefix_Private(NULL, ((PetscObject)dm)->prefix, "-dm_plex_cohesive_label_", &option, NULL, &flg));
4340   if (flg) {
4341     DMLabel   label;
4342     PetscInt  points[1024], n, pStart, pEnd, Nl = 1;
4343     PetscBool noCreate = PETSC_FALSE;
4344     char      fulloption[PETSC_MAX_PATH_LEN];
4345     char      name[PETSC_MAX_PATH_LEN];
4346     size_t    len;
4347 
4348     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4349     PetscCall(PetscStrncpy(name, &option[23], PETSC_MAX_PATH_LEN));
4350     PetscCall(PetscStrlen(name, &len));
4351     if (name[len - 1] == '0') Nl = 10;
4352     for (PetscInt l = 0; l < Nl; ++l) {
4353       if (l > 0) name[len - 1] = '0' + l;
4354       fulloption[0] = 0;
4355       PetscCall(PetscStrlcat(fulloption, "-dm_plex_cohesive_label_", 32));
4356       PetscCall(PetscStrlcat(fulloption, name, PETSC_MAX_PATH_LEN - 32));
4357       n = 1024;
4358       PetscCall(PetscOptionsGetIntArray(NULL, ((PetscObject)dm)->prefix, fulloption, points, &n, &flg));
4359       if (!flg) break;
4360       PetscCall(DMHasLabel(dm, name, &noCreate));
4361       if (noCreate) {
4362         DMLabel         inlabel;
4363         IS              pointIS;
4364         const PetscInt *lpoints;
4365         PetscInt        pdep, ln, inval = points[0];
4366         char            newname[PETSC_MAX_PATH_LEN];
4367 
4368         PetscCheck(n == 1, comm, PETSC_ERR_ARG_WRONG, "Must specify a label value with this option");
4369         PetscCall(DMGetLabel(dm, name, &inlabel));
4370         PetscCall(DMLabelGetStratumIS(inlabel, inval, &pointIS));
4371         PetscCall(ISGetLocalSize(pointIS, &ln));
4372         PetscCall(ISGetIndices(pointIS, &lpoints));
4373         PetscCall(DMPlexGetPointDepth(dm, lpoints[0], &pdep));
4374         PetscCall(PetscSNPrintf(newname, PETSC_MAX_PATH_LEN, "%s%" PetscInt_FMT, name, points[0]));
4375         PetscCall(DMCreateLabel(dm, newname));
4376         PetscCall(DMGetLabel(dm, newname, &label));
4377         if (!pdep) PetscCall(ProcessCohesiveLabel_Vertices(dm, label, inlabel, inval, ln, lpoints));
4378         else PetscCall(ProcessCohesiveLabel_Faces(dm, label, ln, lpoints));
4379         PetscCall(ISRestoreIndices(pointIS, &lpoints));
4380         PetscCall(ISDestroy(&pointIS));
4381       } else {
4382         PetscCall(DMCreateLabel(dm, name));
4383         PetscCall(DMGetLabel(dm, name, &label));
4384         if (pStart >= pEnd) n = 0;
4385         PetscCall(ProcessCohesiveLabel_Faces(dm, label, n, points));
4386       }
4387       PetscCall(DMPlexOrientLabel(dm, label));
4388       PetscCall(DMPlexLabelCohesiveComplete(dm, label, NULL, 1, PETSC_FALSE, PETSC_FALSE, NULL));
4389     }
4390   }
4391   PetscCall(DMViewFromOptions(dm, NULL, "-created_dm_view"));
4392   PetscCall(PetscLogEventEnd(DMPLEX_CreateFromOptions, dm, 0, 0, 0));
4393   PetscFunctionReturn(PETSC_SUCCESS);
4394 }
4395 
4396 PetscErrorCode DMSetFromOptions_NonRefinement_Plex(DM dm, PetscOptionItems *PetscOptionsObject)
4397 {
4398   DM_Plex  *mesh = (DM_Plex *)dm->data;
4399   PetscBool flg, flg2;
4400   char      bdLabel[PETSC_MAX_PATH_LEN];
4401   char      method[PETSC_MAX_PATH_LEN];
4402 
4403   PetscFunctionBegin;
4404   /* Handle viewing */
4405   PetscCall(PetscOptionsBool("-dm_plex_print_set_values", "Output all set values info", "DMPlexMatSetClosure", PETSC_FALSE, &mesh->printSetValues, NULL));
4406   PetscCall(PetscOptionsBoundedInt("-dm_plex_print_fem", "Debug output level for all fem computations", "DMPlexSNESComputeResidualFEM", 0, &mesh->printFEM, NULL, 0));
4407   PetscCall(PetscOptionsBoundedInt("-dm_plex_print_fvm", "Debug output level for all fvm computations", "DMPlexSNESComputeResidualFVM", 0, &mesh->printFVM, NULL, 0));
4408   PetscCall(PetscOptionsReal("-dm_plex_print_tol", "Tolerance for FEM output", "DMPlexSNESComputeResidualFEM", mesh->printTol, &mesh->printTol, NULL));
4409   PetscCall(PetscOptionsBoundedInt("-dm_plex_print_l2", "Debug output level all L2 diff computations", "DMComputeL2Diff", 0, &mesh->printL2, NULL, 0));
4410   PetscCall(PetscOptionsBoundedInt("-dm_plex_print_locate", "Debug output level all point location computations", "DMLocatePoints", 0, &mesh->printLocate, NULL, 0));
4411   PetscCall(DMMonitorSetFromOptions(dm, "-dm_plex_monitor_throughput", "Monitor the simulation throughput", "DMPlexMonitorThroughput", DMPlexMonitorThroughput, NULL, &flg));
4412   if (flg) PetscCall(PetscLogDefaultBegin());
4413   /* Labeling */
4414   PetscCall(PetscOptionsString("-dm_plex_boundary_label", "Label to mark the mesh boundary", "", bdLabel, bdLabel, sizeof(bdLabel), &flg));
4415   if (flg) PetscCall(DMPlexCreateBoundaryLabel_Private(dm, bdLabel));
4416   /* Point Location */
4417   PetscCall(PetscOptionsBool("-dm_plex_hash_location", "Use grid hashing for point location", "DMInterpolate", PETSC_FALSE, &mesh->useHashLocation, NULL));
4418   /* Partitioning and distribution */
4419   PetscCall(PetscOptionsBool("-dm_plex_partition_balance", "Attempt to evenly divide points on partition boundary between processes", "DMPlexSetPartitionBalance", PETSC_FALSE, &mesh->partitionBalance, NULL));
4420   /* Reordering */
4421   PetscCall(PetscOptionsBool("-dm_reorder_section", "Compute point permutation for local section", "DMReorderSectionSetDefault", PETSC_FALSE, &flg2, &flg));
4422   if (flg) PetscCall(DMReorderSectionSetDefault(dm, flg2 ? DM_REORDER_DEFAULT_TRUE : DM_REORDER_DEFAULT_FALSE));
4423   PetscCall(PetscOptionsString("-dm_reorder_section_type", "Reordering method for local section", "DMReorderSectionSetType", method, method, PETSC_MAX_PATH_LEN, &flg));
4424   if (flg) PetscCall(DMReorderSectionSetType(dm, method));
4425   /* Generation and remeshing */
4426   PetscCall(PetscOptionsBool("-dm_plex_remesh_bd", "Allow changes to the boundary on remeshing", "DMAdapt", PETSC_FALSE, &mesh->remeshBd, NULL));
4427   /* Projection behavior */
4428   PetscCall(PetscOptionsBoundedInt("-dm_plex_max_projection_height", "Maximum mesh point height used to project locally", "DMPlexSetMaxProjectionHeight", 0, &mesh->maxProjectionHeight, NULL, 0));
4429   PetscCall(PetscOptionsBool("-dm_plex_regular_refinement", "Use special nested projection algorithm for regular refinement", "DMPlexSetRegularRefinement", mesh->regularRefinement, &mesh->regularRefinement, NULL));
4430   /* Checking structure */
4431   {
4432     PetscBool all = PETSC_FALSE;
4433 
4434     PetscCall(PetscOptionsBool("-dm_plex_check_all", "Perform all basic checks", "DMPlexCheck", PETSC_FALSE, &all, NULL));
4435     if (all) {
4436       PetscCall(DMPlexCheck(dm));
4437     } else {
4438       PetscCall(PetscOptionsBool("-dm_plex_check_symmetry", "Check that the adjacency information in the mesh is symmetric", "DMPlexCheckSymmetry", PETSC_FALSE, &flg, &flg2));
4439       if (flg && flg2) PetscCall(DMPlexCheckSymmetry(dm));
4440       PetscCall(PetscOptionsBool("-dm_plex_check_skeleton", "Check that each cell has the correct number of vertices (only for homogeneous simplex or tensor meshes)", "DMPlexCheckSkeleton", PETSC_FALSE, &flg, &flg2));
4441       if (flg && flg2) PetscCall(DMPlexCheckSkeleton(dm, 0));
4442       PetscCall(PetscOptionsBool("-dm_plex_check_faces", "Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type", "DMPlexCheckFaces", PETSC_FALSE, &flg, &flg2));
4443       if (flg && flg2) PetscCall(DMPlexCheckFaces(dm, 0));
4444       PetscCall(PetscOptionsBool("-dm_plex_check_geometry", "Check that cells have positive volume", "DMPlexCheckGeometry", PETSC_FALSE, &flg, &flg2));
4445       if (flg && flg2) PetscCall(DMPlexCheckGeometry(dm));
4446       PetscCall(PetscOptionsBool("-dm_plex_check_pointsf", "Check some necessary conditions for PointSF", "DMPlexCheckPointSF", PETSC_FALSE, &flg, &flg2));
4447       if (flg && flg2) PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
4448       PetscCall(PetscOptionsBool("-dm_plex_check_interface_cones", "Check points on inter-partition interfaces have conforming order of cone points", "DMPlexCheckInterfaceCones", PETSC_FALSE, &flg, &flg2));
4449       if (flg && flg2) PetscCall(DMPlexCheckInterfaceCones(dm));
4450     }
4451     PetscCall(PetscOptionsBool("-dm_plex_check_cell_shape", "Check cell shape", "DMPlexCheckCellShape", PETSC_FALSE, &flg, &flg2));
4452     if (flg && flg2) PetscCall(DMPlexCheckCellShape(dm, PETSC_TRUE, PETSC_DETERMINE));
4453   }
4454   {
4455     PetscReal scale = 1.0;
4456 
4457     PetscCall(PetscOptionsReal("-dm_plex_scale", "Scale factor for mesh coordinates", "DMPlexScale", scale, &scale, &flg));
4458     if (flg) {
4459       Vec coordinates, coordinatesLocal;
4460 
4461       PetscCall(DMGetCoordinates(dm, &coordinates));
4462       PetscCall(DMGetCoordinatesLocal(dm, &coordinatesLocal));
4463       PetscCall(VecScale(coordinates, scale));
4464       PetscCall(VecScale(coordinatesLocal, scale));
4465     }
4466   }
4467   PetscCall(PetscPartitionerSetFromOptions(mesh->partitioner));
4468   PetscFunctionReturn(PETSC_SUCCESS);
4469 }
4470 
4471 PetscErrorCode DMSetFromOptions_Overlap_Plex(DM dm, PetscOptionItems *PetscOptionsObject, PetscInt *overlap)
4472 {
4473   PetscInt  numOvLabels = 16, numOvExLabels = 16;
4474   char     *ovLabelNames[16], *ovExLabelNames[16];
4475   PetscInt  numOvValues = 16, numOvExValues = 16, l;
4476   PetscBool flg;
4477 
4478   PetscFunctionBegin;
4479   PetscCall(PetscOptionsBoundedInt("-dm_distribute_overlap", "The size of the overlap halo", "DMPlexDistribute", *overlap, overlap, NULL, 0));
4480   PetscCall(PetscOptionsStringArray("-dm_distribute_overlap_labels", "List of overlap label names", "DMPlexDistribute", ovLabelNames, &numOvLabels, &flg));
4481   if (!flg) numOvLabels = 0;
4482   if (numOvLabels) {
4483     ((DM_Plex *)dm->data)->numOvLabels = numOvLabels;
4484     for (l = 0; l < numOvLabels; ++l) {
4485       PetscCall(DMGetLabel(dm, ovLabelNames[l], &((DM_Plex *)dm->data)->ovLabels[l]));
4486       PetscCheck(((DM_Plex *)dm->data)->ovLabels[l], PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Invalid label name %s", ovLabelNames[l]);
4487       PetscCall(PetscFree(ovLabelNames[l]));
4488     }
4489     PetscCall(PetscOptionsIntArray("-dm_distribute_overlap_values", "List of overlap label values", "DMPlexDistribute", ((DM_Plex *)dm->data)->ovValues, &numOvValues, &flg));
4490     if (!flg) numOvValues = 0;
4491     PetscCheck(numOvLabels == numOvValues, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "The number of labels %" PetscInt_FMT " must match the number of values %" PetscInt_FMT, numOvLabels, numOvValues);
4492 
4493     PetscCall(PetscOptionsStringArray("-dm_distribute_overlap_exclude_labels", "List of overlap exclude label names", "DMPlexDistribute", ovExLabelNames, &numOvExLabels, &flg));
4494     if (!flg) numOvExLabels = 0;
4495     ((DM_Plex *)dm->data)->numOvExLabels = numOvExLabels;
4496     for (l = 0; l < numOvExLabels; ++l) {
4497       PetscCall(DMGetLabel(dm, ovExLabelNames[l], &((DM_Plex *)dm->data)->ovExLabels[l]));
4498       PetscCheck(((DM_Plex *)dm->data)->ovExLabels[l], PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Invalid label name %s", ovExLabelNames[l]);
4499       PetscCall(PetscFree(ovExLabelNames[l]));
4500     }
4501     PetscCall(PetscOptionsIntArray("-dm_distribute_overlap_exclude_values", "List of overlap exclude label values", "DMPlexDistribute", ((DM_Plex *)dm->data)->ovExValues, &numOvExValues, &flg));
4502     if (!flg) numOvExValues = 0;
4503     PetscCheck(numOvExLabels == numOvExValues, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "The number of exclude labels %" PetscInt_FMT " must match the number of values %" PetscInt_FMT, numOvExLabels, numOvExValues);
4504   }
4505   PetscFunctionReturn(PETSC_SUCCESS);
4506 }
4507 
4508 static PetscErrorCode DMSetFromOptions_Plex(DM dm, PetscOptionItems *PetscOptionsObject)
4509 {
4510   PetscFunctionList    ordlist;
4511   char                 oname[256];
4512   char                 sublabelname[PETSC_MAX_PATH_LEN] = "";
4513   DMReorderDefaultFlag reorder;
4514   PetscReal            volume    = -1.0;
4515   PetscInt             prerefine = 0, refine = 0, r, coarsen = 0, overlap = 0, extLayers = 0, dim;
4516   PetscBool            uniformOrig = PETSC_FALSE, created = PETSC_FALSE, uniform = PETSC_TRUE, distribute, saveSF = PETSC_FALSE, interpolate = PETSC_TRUE, coordSpace = PETSC_TRUE, remap = PETSC_TRUE, ghostCells = PETSC_FALSE, isHierarchy, flg;
4517 
4518   PetscFunctionBegin;
4519   PetscOptionsHeadBegin(PetscOptionsObject, "DMPlex Options");
4520   if (dm->cloneOpts) goto non_refine;
4521   /* Handle automatic creation */
4522   PetscCall(DMGetDimension(dm, &dim));
4523   if (dim < 0) {
4524     PetscCall(DMPlexCreateFromOptions_Internal(PetscOptionsObject, &coordSpace, dm));
4525     created = PETSC_TRUE;
4526   }
4527   PetscCall(DMGetDimension(dm, &dim));
4528   /* Handle interpolation before distribution */
4529   PetscCall(PetscOptionsBool("-dm_plex_interpolate_pre", "Flag to interpolate mesh before distribution", "", interpolate, &interpolate, &flg));
4530   if (flg) {
4531     DMPlexInterpolatedFlag interpolated;
4532 
4533     PetscCall(DMPlexIsInterpolated(dm, &interpolated));
4534     if (interpolated == DMPLEX_INTERPOLATED_FULL && !interpolate) {
4535       DM udm;
4536 
4537       PetscCall(DMPlexUninterpolate(dm, &udm));
4538       PetscCall(DMPlexReplace_Internal(dm, &udm));
4539     } else if (interpolated != DMPLEX_INTERPOLATED_FULL && interpolate) {
4540       DM idm;
4541 
4542       PetscCall(DMPlexInterpolate(dm, &idm));
4543       PetscCall(DMPlexReplace_Internal(dm, &idm));
4544     }
4545   }
4546   // Handle submesh selection before distribution
4547   PetscCall(PetscOptionsString("-dm_plex_submesh", "Label to use for submesh selection", "", sublabelname, sublabelname, PETSC_MAX_PATH_LEN, &flg));
4548   if (flg) {
4549     DM              subdm;
4550     DMLabel         label;
4551     IS              valueIS, pointIS;
4552     const PetscInt *values, *points;
4553     PetscBool       markedFaces = PETSC_FALSE;
4554     PetscInt        Nv, value, Np;
4555 
4556     PetscCall(DMGetLabel(dm, sublabelname, &label));
4557     PetscCall(DMLabelGetNumValues(label, &Nv));
4558     PetscCheck(Nv == 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Only a single label value is currently supported for submesh selection, not %" PetscInt_FMT, Nv);
4559     PetscCall(DMLabelGetValueIS(label, &valueIS));
4560     PetscCall(ISGetIndices(valueIS, &values));
4561     value = values[0];
4562     PetscCall(ISRestoreIndices(valueIS, &values));
4563     PetscCall(ISDestroy(&valueIS));
4564     PetscCall(DMLabelGetStratumSize(label, value, &Np));
4565     PetscCall(DMLabelGetStratumIS(label, value, &pointIS));
4566     PetscCall(ISGetIndices(pointIS, &points));
4567     for (PetscInt p = 0; p < Np; ++p) {
4568       PetscInt pdepth;
4569 
4570       PetscCall(DMPlexGetPointDepth(dm, points[p], &pdepth));
4571       if (pdepth) {
4572         markedFaces = PETSC_TRUE;
4573         break;
4574       }
4575     }
4576     PetscCall(ISRestoreIndices(pointIS, &points));
4577     PetscCall(ISDestroy(&pointIS));
4578     PetscCall(DMPlexCreateSubmesh(dm, label, value, markedFaces, &subdm));
4579     PetscCall(DMPlexReplace_Internal(dm, &subdm));
4580     PetscCall(DMSetFromOptions_NonRefinement_Plex(dm, PetscOptionsObject));
4581   }
4582   /* Handle DMPlex refinement before distribution */
4583   PetscCall(DMPlexGetRefinementUniform(dm, &uniformOrig));
4584   PetscCall(PetscOptionsBoundedInt("-dm_refine_pre", "The number of refinements before distribution", "DMCreate", prerefine, &prerefine, NULL, 0));
4585   PetscCall(PetscOptionsBool("-dm_refine_remap_pre", "Flag to control coordinate remapping", "DMCreate", remap, &remap, NULL));
4586   PetscCall(PetscOptionsBool("-dm_refine_uniform_pre", "Flag for uniform refinement before distribution", "DMCreate", uniform, &uniform, &flg));
4587   if (flg) PetscCall(DMPlexSetRefinementUniform(dm, uniform));
4588   PetscCall(PetscOptionsReal("-dm_refine_volume_limit_pre", "The maximum cell volume after refinement before distribution", "DMCreate", volume, &volume, &flg));
4589   if (flg) {
4590     PetscCall(DMPlexSetRefinementUniform(dm, PETSC_FALSE));
4591     PetscCall(DMPlexSetRefinementLimit(dm, volume));
4592     prerefine = PetscMax(prerefine, 1);
4593   }
4594   if (prerefine) PetscCall(DMLocalizeCoordinates(dm));
4595   for (r = 0; r < prerefine; ++r) {
4596     DM             rdm;
4597     PetscPointFunc coordFunc = ((DM_Plex *)dm->data)->coordFunc;
4598 
4599     PetscCall(DMSetFromOptions_NonRefinement_Plex(dm, PetscOptionsObject));
4600     PetscCall(DMRefine(dm, PetscObjectComm((PetscObject)dm), &rdm));
4601     PetscCall(DMPlexReplace_Internal(dm, &rdm));
4602     PetscCall(DMSetFromOptions_NonRefinement_Plex(dm, PetscOptionsObject));
4603     if (coordFunc && remap) {
4604       PetscCall(DMPlexRemapGeometry(dm, 0.0, coordFunc));
4605       ((DM_Plex *)dm->data)->coordFunc = coordFunc;
4606     }
4607   }
4608   PetscCall(DMPlexSetRefinementUniform(dm, uniformOrig));
4609   /* Handle DMPlex extrusion before distribution */
4610   PetscCall(PetscOptionsBoundedInt("-dm_extrude", "The number of layers to extrude", "", extLayers, &extLayers, NULL, 0));
4611   if (extLayers) {
4612     DM edm;
4613 
4614     PetscCall(DMExtrude(dm, extLayers, &edm));
4615     PetscCall(DMPlexReplace_Internal(dm, &edm));
4616     ((DM_Plex *)dm->data)->coordFunc = NULL;
4617     PetscCall(DMSetFromOptions_NonRefinement_Plex(dm, PetscOptionsObject));
4618     extLayers = 0;
4619     PetscCall(DMGetDimension(dm, &dim));
4620   }
4621   /* Handle DMPlex reordering before distribution */
4622   PetscCall(DMPlexReorderGetDefault(dm, &reorder));
4623   PetscCall(MatGetOrderingList(&ordlist));
4624   PetscCall(PetscStrncpy(oname, MATORDERINGNATURAL, sizeof(oname)));
4625   PetscCall(PetscOptionsFList("-dm_plex_reorder", "Set mesh reordering type", "DMPlexGetOrdering", ordlist, MATORDERINGNATURAL, oname, sizeof(oname), &flg));
4626   if (reorder == DM_REORDER_DEFAULT_TRUE || flg) {
4627     DM pdm;
4628     IS perm;
4629 
4630     PetscCall(DMPlexGetOrdering(dm, oname, NULL, &perm));
4631     PetscCall(DMPlexPermute(dm, perm, &pdm));
4632     PetscCall(ISDestroy(&perm));
4633     PetscCall(DMPlexReplace_Internal(dm, &pdm));
4634     PetscCall(DMSetFromOptions_NonRefinement_Plex(dm, PetscOptionsObject));
4635   }
4636   /* Handle DMPlex distribution */
4637   PetscCall(DMPlexDistributeGetDefault(dm, &distribute));
4638   PetscCall(PetscOptionsBool("-dm_distribute", "Flag to redistribute a mesh among processes", "DMPlexDistribute", distribute, &distribute, NULL));
4639   PetscCall(PetscOptionsBool("-dm_distribute_save_sf", "Flag to save the migration SF", "DMPlexSetMigrationSF", saveSF, &saveSF, NULL));
4640   PetscCall(DMSetFromOptions_Overlap_Plex(dm, PetscOptionsObject, &overlap));
4641   if (distribute) {
4642     DM               pdm = NULL;
4643     PetscPartitioner part;
4644     PetscSF          sfMigration;
4645 
4646     PetscCall(DMPlexGetPartitioner(dm, &part));
4647     PetscCall(PetscPartitionerSetFromOptions(part));
4648     PetscCall(DMPlexDistribute(dm, overlap, &sfMigration, &pdm));
4649     if (pdm) PetscCall(DMPlexReplace_Internal(dm, &pdm));
4650     if (saveSF) PetscCall(DMPlexSetMigrationSF(dm, sfMigration));
4651     PetscCall(PetscSFDestroy(&sfMigration));
4652   }
4653 
4654   {
4655     PetscBool useBoxLabel = PETSC_FALSE;
4656     PetscCall(PetscOptionsBool("-dm_plex_box_label", "Create 'Face Sets' assuming boundary faces align with cartesian directions", "DMCreate", useBoxLabel, &useBoxLabel, NULL));
4657     if (useBoxLabel) PetscCall(DMPlexSetBoxLabel_Internal(dm));
4658   }
4659   /* Must check CEED options before creating function space for coordinates */
4660   {
4661     PetscBool useCeed = PETSC_FALSE, flg;
4662 
4663     PetscCall(PetscOptionsBool("-dm_plex_use_ceed", "Use LibCEED as the FEM backend", "DMPlexSetUseCeed", useCeed, &useCeed, &flg));
4664     if (flg) PetscCall(DMPlexSetUseCeed(dm, useCeed));
4665   }
4666   /* Create coordinate space */
4667   if (created) {
4668     DM_Plex  *mesh   = (DM_Plex *)dm->data;
4669     PetscInt  degree = 1, deg;
4670     PetscInt  height = 0;
4671     DM        cdm;
4672     PetscBool flg, localize = PETSC_TRUE, sparseLocalize = PETSC_TRUE;
4673 
4674     PetscCall(PetscOptionsBool("-dm_coord_space", "Use an FEM space for coordinates", "", coordSpace, &coordSpace, &flg));
4675     PetscCall(PetscOptionsInt("-dm_coord_petscspace_degree", "FEM degree for coordinate space", "", degree, &degree, NULL));
4676     PetscCall(DMGetCoordinateDegree_Internal(dm, &deg));
4677     if (coordSpace && deg <= 1) PetscCall(DMPlexCreateCoordinateSpace(dm, degree, PETSC_TRUE, mesh->coordFunc));
4678     PetscCall(DMGetCoordinateDM(dm, &cdm));
4679     if (flg && !coordSpace) {
4680       PetscDS      cds;
4681       PetscObject  obj;
4682       PetscClassId id;
4683 
4684       PetscCall(DMGetDS(cdm, &cds));
4685       PetscCall(PetscDSGetDiscretization(cds, 0, &obj));
4686       PetscCall(PetscObjectGetClassId(obj, &id));
4687       if (id == PETSCFE_CLASSID) {
4688         PetscContainer dummy;
4689 
4690         PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &dummy));
4691         PetscCall(PetscObjectSetName((PetscObject)dummy, "coordinates"));
4692         PetscCall(DMSetField(cdm, 0, NULL, (PetscObject)dummy));
4693         PetscCall(PetscContainerDestroy(&dummy));
4694         PetscCall(DMClearDS(cdm));
4695       }
4696       mesh->coordFunc = NULL;
4697     }
4698     PetscCall(PetscOptionsBool("-dm_localize", "Localize mesh coordinates", "", localize, &localize, NULL));
4699     PetscCall(PetscOptionsBool("-dm_sparse_localize", "Localize only necessary cells", "DMSetSparseLocalize", sparseLocalize, &sparseLocalize, &flg));
4700     if (flg) PetscCall(DMSetSparseLocalize(dm, sparseLocalize));
4701     PetscCall(PetscOptionsInt("-dm_localize_height", "Localize edges and faces in addition to cells", "", height, &height, &flg));
4702     if (flg) PetscCall(DMPlexSetMaxProjectionHeight(cdm, height));
4703     if (localize) PetscCall(DMLocalizeCoordinates(dm));
4704   }
4705   /* Handle DMPlex refinement */
4706   remap = PETSC_TRUE;
4707   PetscCall(PetscOptionsBoundedInt("-dm_refine", "The number of uniform refinements", "DMCreate", refine, &refine, NULL, 0));
4708   PetscCall(PetscOptionsBool("-dm_refine_remap", "Flag to control coordinate remapping", "DMCreate", remap, &remap, NULL));
4709   PetscCall(PetscOptionsBoundedInt("-dm_refine_hierarchy", "The number of uniform refinements", "DMCreate", refine, &refine, &isHierarchy, 0));
4710   if (refine) PetscCall(DMPlexSetRefinementUniform(dm, PETSC_TRUE));
4711   if (refine && isHierarchy) {
4712     DM *dms, coarseDM;
4713 
4714     PetscCall(DMGetCoarseDM(dm, &coarseDM));
4715     PetscCall(PetscObjectReference((PetscObject)coarseDM));
4716     PetscCall(PetscMalloc1(refine, &dms));
4717     PetscCall(DMRefineHierarchy(dm, refine, dms));
4718     /* Total hack since we do not pass in a pointer */
4719     PetscCall(DMPlexSwap_Static(dm, dms[refine - 1]));
4720     if (refine == 1) {
4721       PetscCall(DMSetCoarseDM(dm, dms[0]));
4722       PetscCall(DMPlexSetRegularRefinement(dm, PETSC_TRUE));
4723     } else {
4724       PetscCall(DMSetCoarseDM(dm, dms[refine - 2]));
4725       PetscCall(DMPlexSetRegularRefinement(dm, PETSC_TRUE));
4726       PetscCall(DMSetCoarseDM(dms[0], dms[refine - 1]));
4727       PetscCall(DMPlexSetRegularRefinement(dms[0], PETSC_TRUE));
4728     }
4729     PetscCall(DMSetCoarseDM(dms[refine - 1], coarseDM));
4730     PetscCall(PetscObjectDereference((PetscObject)coarseDM));
4731     /* Free DMs */
4732     for (r = 0; r < refine; ++r) {
4733       PetscCall(DMSetFromOptions_NonRefinement_Plex(dms[r], PetscOptionsObject));
4734       PetscCall(DMDestroy(&dms[r]));
4735     }
4736     PetscCall(PetscFree(dms));
4737   } else {
4738     for (r = 0; r < refine; ++r) {
4739       DM             rdm;
4740       PetscPointFunc coordFunc = ((DM_Plex *)dm->data)->coordFunc;
4741 
4742       PetscCall(DMSetFromOptions_NonRefinement_Plex(dm, PetscOptionsObject));
4743       PetscCall(DMRefine(dm, PetscObjectComm((PetscObject)dm), &rdm));
4744       /* Total hack since we do not pass in a pointer */
4745       PetscCall(DMPlexReplace_Internal(dm, &rdm));
4746       PetscCall(DMSetFromOptions_NonRefinement_Plex(dm, PetscOptionsObject));
4747       if (coordFunc && remap) {
4748         PetscCall(DMPlexRemapGeometry(dm, 0.0, coordFunc));
4749         ((DM_Plex *)dm->data)->coordFunc = coordFunc;
4750       }
4751     }
4752   }
4753   /* Handle DMPlex coarsening */
4754   PetscCall(PetscOptionsBoundedInt("-dm_coarsen", "Coarsen the mesh", "DMCreate", coarsen, &coarsen, NULL, 0));
4755   PetscCall(PetscOptionsBoundedInt("-dm_coarsen_hierarchy", "The number of coarsenings", "DMCreate", coarsen, &coarsen, &isHierarchy, 0));
4756   if (coarsen && isHierarchy) {
4757     DM *dms;
4758 
4759     PetscCall(PetscMalloc1(coarsen, &dms));
4760     PetscCall(DMCoarsenHierarchy(dm, coarsen, dms));
4761     /* Free DMs */
4762     for (r = 0; r < coarsen; ++r) {
4763       PetscCall(DMSetFromOptions_NonRefinement_Plex(dms[r], PetscOptionsObject));
4764       PetscCall(DMDestroy(&dms[r]));
4765     }
4766     PetscCall(PetscFree(dms));
4767   } else {
4768     for (r = 0; r < coarsen; ++r) {
4769       DM             cdm;
4770       PetscPointFunc coordFunc = ((DM_Plex *)dm->data)->coordFunc;
4771 
4772       PetscCall(DMSetFromOptions_NonRefinement_Plex(dm, PetscOptionsObject));
4773       PetscCall(DMCoarsen(dm, PetscObjectComm((PetscObject)dm), &cdm));
4774       /* Total hack since we do not pass in a pointer */
4775       PetscCall(DMPlexReplace_Internal(dm, &cdm));
4776       PetscCall(DMSetFromOptions_NonRefinement_Plex(dm, PetscOptionsObject));
4777       if (coordFunc) {
4778         PetscCall(DMPlexRemapGeometry(dm, 0.0, coordFunc));
4779         ((DM_Plex *)dm->data)->coordFunc = coordFunc;
4780       }
4781     }
4782   }
4783   // Handle coordinate remapping
4784   remap = PETSC_FALSE;
4785   PetscCall(PetscOptionsBool("-dm_coord_remap", "Flag to control coordinate remapping", "", remap, &remap, NULL));
4786   if (remap) {
4787     DMPlexCoordMap map     = DM_COORD_MAP_NONE;
4788     PetscPointFunc mapFunc = NULL;
4789     PetscScalar    params[16];
4790     PetscInt       Np = PETSC_STATIC_ARRAY_LENGTH(params), cdim;
4791     MPI_Comm       comm;
4792 
4793     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
4794     PetscCall(DMGetCoordinateDim(dm, &cdim));
4795     PetscCall(PetscOptionsScalarArray("-dm_coord_map_params", "Parameters for the coordinate remapping", "", params, &Np, &flg));
4796     if (!flg) Np = 0;
4797     // TODO Allow user to pass a map function by name
4798     PetscCall(PetscOptionsEnum("-dm_coord_map", "Coordinate mapping for built-in mesh", "", DMPlexCoordMaps, (PetscEnum)map, (PetscEnum *)&map, &flg));
4799     if (flg) {
4800       switch (map) {
4801       case DM_COORD_MAP_NONE:
4802         mapFunc = coordMap_identity;
4803         break;
4804       case DM_COORD_MAP_SHEAR:
4805         mapFunc = coordMap_shear;
4806         if (!Np) {
4807           Np        = cdim + 1;
4808           params[0] = 0;
4809           for (PetscInt d = 1; d <= cdim; ++d) params[d] = 1.0;
4810         }
4811         PetscCheck(Np == cdim + 1, comm, PETSC_ERR_ARG_WRONG, "The shear coordinate map must have cdim + 1 = %" PetscInt_FMT " parameters, not %" PetscInt_FMT, cdim + 1, Np);
4812         break;
4813       case DM_COORD_MAP_FLARE:
4814         mapFunc = coordMap_flare;
4815         if (!Np) {
4816           Np        = cdim + 1;
4817           params[0] = 0;
4818           for (PetscInt d = 1; d <= cdim; ++d) params[d] = 1.0;
4819         }
4820         PetscCheck(Np == cdim + 1, comm, PETSC_ERR_ARG_WRONG, "The flare coordinate map must have cdim + 1 = %" PetscInt_FMT " parameters, not %" PetscInt_FMT, cdim + 1, Np);
4821         break;
4822       case DM_COORD_MAP_ANNULUS:
4823         mapFunc = coordMap_annulus;
4824         if (!Np) {
4825           Np        = 2;
4826           params[0] = 1.;
4827           params[1] = 2.;
4828         }
4829         PetscCheck(Np == 2, comm, PETSC_ERR_ARG_WRONG, "The annulus coordinate map must have 2 parameters, not %" PetscInt_FMT, Np);
4830         break;
4831       case DM_COORD_MAP_SHELL:
4832         mapFunc = coordMap_shell;
4833         if (!Np) {
4834           Np        = 2;
4835           params[0] = 1.;
4836           params[1] = 2.;
4837         }
4838         PetscCheck(Np == 2, comm, PETSC_ERR_ARG_WRONG, "The spherical shell coordinate map must have 2 parameters, not %" PetscInt_FMT, Np);
4839         break;
4840       default:
4841         mapFunc = coordMap_identity;
4842       }
4843     }
4844     if (Np) {
4845       DM      cdm;
4846       PetscDS cds;
4847 
4848       PetscCall(DMGetCoordinateDM(dm, &cdm));
4849       PetscCall(DMGetDS(cdm, &cds));
4850       PetscCall(PetscDSSetConstants(cds, Np, params));
4851     }
4852     PetscCall(DMPlexRemapGeometry(dm, 0.0, mapFunc));
4853   }
4854   /* Handle ghost cells */
4855   PetscCall(PetscOptionsBool("-dm_plex_create_fv_ghost_cells", "Flag to create finite volume ghost cells on the boundary", "DMCreate", ghostCells, &ghostCells, NULL));
4856   if (ghostCells) {
4857     DM   gdm;
4858     char lname[PETSC_MAX_PATH_LEN];
4859 
4860     lname[0] = '\0';
4861     PetscCall(PetscOptionsString("-dm_plex_fv_ghost_cells_label", "Label name for ghost cells boundary", "DMCreate", lname, lname, sizeof(lname), &flg));
4862     PetscCall(DMPlexConstructGhostCells(dm, flg ? lname : NULL, NULL, &gdm));
4863     PetscCall(DMPlexReplace_Internal(dm, &gdm));
4864   }
4865   /* Handle 1D order */
4866   if (reorder != DM_REORDER_DEFAULT_FALSE && dim == 1) {
4867     DM           cdm, rdm;
4868     PetscDS      cds;
4869     PetscObject  obj;
4870     PetscClassId id = PETSC_OBJECT_CLASSID;
4871     IS           perm;
4872     PetscInt     Nf;
4873     PetscBool    distributed;
4874 
4875     PetscCall(DMPlexIsDistributed(dm, &distributed));
4876     PetscCall(DMGetCoordinateDM(dm, &cdm));
4877     PetscCall(DMGetDS(cdm, &cds));
4878     PetscCall(PetscDSGetNumFields(cds, &Nf));
4879     if (Nf) {
4880       PetscCall(PetscDSGetDiscretization(cds, 0, &obj));
4881       PetscCall(PetscObjectGetClassId(obj, &id));
4882     }
4883     if (!distributed && id != PETSCFE_CLASSID) {
4884       PetscCall(DMPlexGetOrdering1D(dm, &perm));
4885       PetscCall(DMPlexPermute(dm, perm, &rdm));
4886       PetscCall(DMPlexReplace_Internal(dm, &rdm));
4887       PetscCall(ISDestroy(&perm));
4888     }
4889   }
4890 /* Handle */
4891 non_refine:
4892   PetscCall(DMSetFromOptions_NonRefinement_Plex(dm, PetscOptionsObject));
4893   PetscOptionsHeadEnd();
4894   PetscFunctionReturn(PETSC_SUCCESS);
4895 }
4896 
4897 static PetscErrorCode DMCreateGlobalVector_Plex(DM dm, Vec *vec)
4898 {
4899   PetscFunctionBegin;
4900   PetscCall(DMCreateGlobalVector_Section_Private(dm, vec));
4901   /* PetscCall(VecSetOperation(*vec, VECOP_DUPLICATE, (void(*)(void)) VecDuplicate_MPI_DM)); */
4902   PetscCall(VecSetOperation(*vec, VECOP_VIEW, (void (*)(void))VecView_Plex));
4903   PetscCall(VecSetOperation(*vec, VECOP_VIEWNATIVE, (void (*)(void))VecView_Plex_Native));
4904   PetscCall(VecSetOperation(*vec, VECOP_LOAD, (void (*)(void))VecLoad_Plex));
4905   PetscCall(VecSetOperation(*vec, VECOP_LOADNATIVE, (void (*)(void))VecLoad_Plex_Native));
4906   PetscFunctionReturn(PETSC_SUCCESS);
4907 }
4908 
4909 static PetscErrorCode DMCreateLocalVector_Plex(DM dm, Vec *vec)
4910 {
4911   PetscFunctionBegin;
4912   PetscCall(DMCreateLocalVector_Section_Private(dm, vec));
4913   PetscCall(VecSetOperation(*vec, VECOP_VIEW, (void (*)(void))VecView_Plex_Local));
4914   PetscCall(VecSetOperation(*vec, VECOP_LOAD, (void (*)(void))VecLoad_Plex_Local));
4915   PetscFunctionReturn(PETSC_SUCCESS);
4916 }
4917 
4918 static PetscErrorCode DMGetDimPoints_Plex(DM dm, PetscInt dim, PetscInt *pStart, PetscInt *pEnd)
4919 {
4920   PetscInt depth, d;
4921 
4922   PetscFunctionBegin;
4923   PetscCall(DMPlexGetDepth(dm, &depth));
4924   if (depth == 1) {
4925     PetscCall(DMGetDimension(dm, &d));
4926     if (dim == 0) PetscCall(DMPlexGetDepthStratum(dm, dim, pStart, pEnd));
4927     else if (dim == d) PetscCall(DMPlexGetDepthStratum(dm, 1, pStart, pEnd));
4928     else {
4929       *pStart = 0;
4930       *pEnd   = 0;
4931     }
4932   } else {
4933     PetscCall(DMPlexGetDepthStratum(dm, dim, pStart, pEnd));
4934   }
4935   PetscFunctionReturn(PETSC_SUCCESS);
4936 }
4937 
4938 static PetscErrorCode DMGetNeighbors_Plex(DM dm, PetscInt *nranks, const PetscMPIInt *ranks[])
4939 {
4940   PetscSF            sf;
4941   PetscInt           niranks, njranks, n;
4942   const PetscMPIInt *iranks, *jranks;
4943   DM_Plex           *data = (DM_Plex *)dm->data;
4944 
4945   PetscFunctionBegin;
4946   PetscCall(DMGetPointSF(dm, &sf));
4947   if (!data->neighbors) {
4948     PetscCall(PetscSFSetUp(sf));
4949     PetscCall(PetscSFGetRootRanks(sf, &njranks, &jranks, NULL, NULL, NULL));
4950     PetscCall(PetscSFGetLeafRanks(sf, &niranks, &iranks, NULL, NULL));
4951     PetscCall(PetscMalloc1(njranks + niranks + 1, &data->neighbors));
4952     PetscCall(PetscArraycpy(data->neighbors + 1, jranks, njranks));
4953     PetscCall(PetscArraycpy(data->neighbors + njranks + 1, iranks, niranks));
4954     n = njranks + niranks;
4955     PetscCall(PetscSortRemoveDupsMPIInt(&n, data->neighbors + 1));
4956     /* The following cast should never fail: can't have more neighbors than PETSC_MPI_INT_MAX */
4957     PetscCall(PetscMPIIntCast(n, data->neighbors));
4958   }
4959   if (nranks) *nranks = data->neighbors[0];
4960   if (ranks) {
4961     if (data->neighbors[0]) *ranks = data->neighbors + 1;
4962     else *ranks = NULL;
4963   }
4964   PetscFunctionReturn(PETSC_SUCCESS);
4965 }
4966 
4967 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM, DM, Mat, Vec, Vec);
4968 
4969 static PetscErrorCode DMInitialize_Plex(DM dm)
4970 {
4971   PetscFunctionBegin;
4972   dm->ops->view                      = DMView_Plex;
4973   dm->ops->load                      = DMLoad_Plex;
4974   dm->ops->setfromoptions            = DMSetFromOptions_Plex;
4975   dm->ops->clone                     = DMClone_Plex;
4976   dm->ops->setup                     = DMSetUp_Plex;
4977   dm->ops->createlocalsection        = DMCreateLocalSection_Plex;
4978   dm->ops->createsectionpermutation  = DMCreateSectionPermutation_Plex;
4979   dm->ops->createdefaultconstraints  = DMCreateDefaultConstraints_Plex;
4980   dm->ops->createglobalvector        = DMCreateGlobalVector_Plex;
4981   dm->ops->createlocalvector         = DMCreateLocalVector_Plex;
4982   dm->ops->getlocaltoglobalmapping   = NULL;
4983   dm->ops->createfieldis             = NULL;
4984   dm->ops->createcoordinatedm        = DMCreateCoordinateDM_Plex;
4985   dm->ops->createcoordinatefield     = DMCreateCoordinateField_Plex;
4986   dm->ops->getcoloring               = NULL;
4987   dm->ops->creatematrix              = DMCreateMatrix_Plex;
4988   dm->ops->createinterpolation       = DMCreateInterpolation_Plex;
4989   dm->ops->createmassmatrix          = DMCreateMassMatrix_Plex;
4990   dm->ops->createmassmatrixlumped    = DMCreateMassMatrixLumped_Plex;
4991   dm->ops->createinjection           = DMCreateInjection_Plex;
4992   dm->ops->refine                    = DMRefine_Plex;
4993   dm->ops->coarsen                   = DMCoarsen_Plex;
4994   dm->ops->refinehierarchy           = DMRefineHierarchy_Plex;
4995   dm->ops->coarsenhierarchy          = DMCoarsenHierarchy_Plex;
4996   dm->ops->extrude                   = DMExtrude_Plex;
4997   dm->ops->globaltolocalbegin        = NULL;
4998   dm->ops->globaltolocalend          = NULL;
4999   dm->ops->localtoglobalbegin        = NULL;
5000   dm->ops->localtoglobalend          = NULL;
5001   dm->ops->destroy                   = DMDestroy_Plex;
5002   dm->ops->createsubdm               = DMCreateSubDM_Plex;
5003   dm->ops->createsuperdm             = DMCreateSuperDM_Plex;
5004   dm->ops->getdimpoints              = DMGetDimPoints_Plex;
5005   dm->ops->locatepoints              = DMLocatePoints_Plex;
5006   dm->ops->projectfunctionlocal      = DMProjectFunctionLocal_Plex;
5007   dm->ops->projectfunctionlabellocal = DMProjectFunctionLabelLocal_Plex;
5008   dm->ops->projectfieldlocal         = DMProjectFieldLocal_Plex;
5009   dm->ops->projectfieldlabellocal    = DMProjectFieldLabelLocal_Plex;
5010   dm->ops->projectbdfieldlabellocal  = DMProjectBdFieldLabelLocal_Plex;
5011   dm->ops->computel2diff             = DMComputeL2Diff_Plex;
5012   dm->ops->computel2gradientdiff     = DMComputeL2GradientDiff_Plex;
5013   dm->ops->computel2fielddiff        = DMComputeL2FieldDiff_Plex;
5014   dm->ops->getneighbors              = DMGetNeighbors_Plex;
5015   dm->ops->getlocalboundingbox       = DMGetLocalBoundingBox_Coordinates;
5016   dm->ops->createdomaindecomposition = DMCreateDomainDecomposition_Plex;
5017   dm->ops->createddscatters          = DMCreateDomainDecompositionScatters_Plex;
5018   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", DMPlexInsertBoundaryValues_Plex));
5019   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerivativeBoundaryValues_C", DMPlexInsertTimeDerivativeBoundaryValues_Plex));
5020   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", DMSetUpGLVisViewer_Plex));
5021   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", DMCreateNeumannOverlap_Plex));
5022   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", DMPlexDistributeGetDefault_Plex));
5023   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", DMPlexDistributeSetDefault_Plex));
5024   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", DMPlexReorderGetDefault_Plex));
5025   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", DMPlexReorderSetDefault_Plex));
5026   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetDefault_C", DMReorderSectionGetDefault_Plex));
5027   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetDefault_C", DMReorderSectionSetDefault_Plex));
5028   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetType_C", DMReorderSectionGetType_Plex));
5029   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetType_C", DMReorderSectionSetType_Plex));
5030   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", DMInterpolateSolution_Plex));
5031   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", DMPlexGetOverlap_Plex));
5032   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", DMPlexSetOverlap_Plex));
5033   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetUseCeed_C", DMPlexGetUseCeed_Plex));
5034   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetUseCeed_C", DMPlexSetUseCeed_Plex));
5035   PetscFunctionReturn(PETSC_SUCCESS);
5036 }
5037 
5038 PETSC_INTERN PetscErrorCode DMClone_Plex(DM dm, DM *newdm)
5039 {
5040   DM_Plex       *mesh = (DM_Plex *)dm->data;
5041   const PetscSF *face_sfs;
5042   PetscInt       num_face_sfs;
5043 
5044   PetscFunctionBegin;
5045   mesh->refct++;
5046   (*newdm)->data = mesh;
5047   PetscCall(DMPlexGetIsoperiodicFaceSF(dm, &num_face_sfs, &face_sfs));
5048   PetscCall(DMPlexSetIsoperiodicFaceSF(*newdm, num_face_sfs, (PetscSF *)face_sfs));
5049   PetscCall(PetscObjectChangeTypeName((PetscObject)*newdm, DMPLEX));
5050   PetscCall(DMInitialize_Plex(*newdm));
5051   PetscFunctionReturn(PETSC_SUCCESS);
5052 }
5053 
5054 /*MC
5055   DMPLEX = "plex" - A `DM` object that encapsulates an unstructured mesh, or CW Complex, which can be expressed using a Hasse Diagram.
5056                     In the local representation, `Vec`s contain all unknowns in the interior and shared boundary. This is
5057                     specified by a PetscSection object. Ownership in the global representation is determined by
5058                     ownership of the underlying `DMPLEX` points. This is specified by another `PetscSection` object.
5059 
5060   Options Database Keys:
5061 + -dm_refine_pre                     - Refine mesh before distribution
5062 + -dm_refine_uniform_pre             - Choose uniform or generator-based refinement
5063 + -dm_refine_volume_limit_pre        - Cell volume limit after pre-refinement using generator
5064 . -dm_distribute                     - Distribute mesh across processes
5065 . -dm_distribute_overlap             - Number of cells to overlap for distribution
5066 . -dm_refine                         - Refine mesh after distribution
5067 . -dm_localize <bool>                - Whether to localize coordinates for periodic meshes
5068 . -dm_sparse_localize <bool>         - Whether to only localize cells on the periodic boundary
5069 . -dm_plex_hash_location             - Use grid hashing for point location
5070 . -dm_plex_hash_box_faces <n,m,p>    - The number of divisions in each direction of the grid hash
5071 . -dm_plex_partition_balance         - Attempt to evenly divide points on partition boundary between processes
5072 . -dm_plex_remesh_bd                 - Allow changes to the boundary on remeshing
5073 . -dm_plex_max_projection_height     - Maximum mesh point height used to project locally
5074 . -dm_plex_regular_refinement        - Use special nested projection algorithm for regular refinement
5075 . -dm_plex_reorder_section           - Use specialized blocking if available
5076 . -dm_plex_check_all                 - Perform all checks below
5077 . -dm_plex_check_symmetry            - Check that the adjacency information in the mesh is symmetric
5078 . -dm_plex_check_skeleton <celltype> - Check that each cell has the correct number of vertices
5079 . -dm_plex_check_faces <celltype>    - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
5080 . -dm_plex_check_geometry            - Check that cells have positive volume
5081 . -dm_view :mesh.tex:ascii_latex     - View the mesh in LaTeX/TikZ
5082 . -dm_plex_view_scale <num>          - Scale the TikZ
5083 . -dm_plex_print_fem <num>           - View FEM assembly information, such as element vectors and matrices
5084 - -dm_plex_print_fvm <num>           - View FVM assembly information, such as flux updates
5085 
5086   Level: intermediate
5087 
5088 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMType`, `DMPlexCreate()`, `DMCreate()`, `DMSetType()`, `PetscSection`
5089 M*/
5090 
5091 PETSC_EXTERN PetscErrorCode DMCreate_Plex(DM dm)
5092 {
5093   DM_Plex *mesh;
5094   PetscInt unit;
5095 
5096   PetscFunctionBegin;
5097   PetscCall(PetscCitationsRegister(PlexCitation, &Plexcite));
5098   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5099   PetscCall(PetscNew(&mesh));
5100   dm->reorderSection = DM_REORDER_DEFAULT_NOTSET;
5101   dm->data           = mesh;
5102 
5103   mesh->refct = 1;
5104   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &mesh->coneSection));
5105   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &mesh->supportSection));
5106   mesh->refinementUniform      = PETSC_TRUE;
5107   mesh->refinementLimit        = -1.0;
5108   mesh->distDefault            = PETSC_TRUE;
5109   mesh->reorderDefault         = DM_REORDER_DEFAULT_NOTSET;
5110   mesh->distributionName       = NULL;
5111   mesh->interpolated           = DMPLEX_INTERPOLATED_INVALID;
5112   mesh->interpolatedCollective = DMPLEX_INTERPOLATED_INVALID;
5113 
5114   PetscCall(PetscPartitionerCreate(PetscObjectComm((PetscObject)dm), &mesh->partitioner));
5115   mesh->remeshBd = PETSC_FALSE;
5116 
5117   for (unit = 0; unit < NUM_PETSC_UNITS; ++unit) mesh->scale[unit] = 1.0;
5118 
5119   mesh->depthState    = -1;
5120   mesh->celltypeState = -1;
5121   mesh->printTol      = 1.0e-10;
5122 
5123   PetscCall(DMInitialize_Plex(dm));
5124   PetscFunctionReturn(PETSC_SUCCESS);
5125 }
5126 
5127 /*@
5128   DMPlexCreate - Creates a `DMPLEX` object, which encapsulates an unstructured mesh, or CW complex, which can be expressed using a Hasse Diagram.
5129 
5130   Collective
5131 
5132   Input Parameter:
5133 . comm - The communicator for the `DMPLEX` object
5134 
5135   Output Parameter:
5136 . mesh - The `DMPLEX` object
5137 
5138   Level: beginner
5139 
5140 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMType`, `DMCreate()`, `DMSetType()`
5141 @*/
5142 PetscErrorCode DMPlexCreate(MPI_Comm comm, DM *mesh)
5143 {
5144   PetscFunctionBegin;
5145   PetscAssertPointer(mesh, 2);
5146   PetscCall(DMCreate(comm, mesh));
5147   PetscCall(DMSetType(*mesh, DMPLEX));
5148   PetscFunctionReturn(PETSC_SUCCESS);
5149 }
5150 
5151 /*@C
5152   DMPlexBuildFromCellListParallel - Build distributed `DMPLEX` topology from a list of vertices for each cell (common mesh generator output)
5153 
5154   Collective; No Fortran Support
5155 
5156   Input Parameters:
5157 + dm          - The `DM`
5158 . numCells    - The number of cells owned by this process
5159 . numVertices - The number of vertices to be owned by this process, or `PETSC_DECIDE`
5160 . NVertices   - The global number of vertices, or `PETSC_DETERMINE`
5161 . numCorners  - The number of vertices for each cell
5162 - cells       - An array of numCells*numCorners numbers, the global vertex numbers for each cell
5163 
5164   Output Parameters:
5165 + vertexSF         - (Optional) `PetscSF` describing complete vertex ownership
5166 - verticesAdjSaved - (Optional) vertex adjacency array
5167 
5168   Level: advanced
5169 
5170   Notes:
5171   Two triangles sharing a face
5172 .vb
5173 
5174         2
5175       / | \
5176      /  |  \
5177     /   |   \
5178    0  0 | 1  3
5179     \   |   /
5180      \  |  /
5181       \ | /
5182         1
5183 .ve
5184   would have input
5185 .vb
5186   numCells = 2, numVertices = 4
5187   cells = [0 1 2  1 3 2]
5188 .ve
5189   which would result in the `DMPLEX`
5190 .vb
5191 
5192         4
5193       / | \
5194      /  |  \
5195     /   |   \
5196    2  0 | 1  5
5197     \   |   /
5198      \  |  /
5199       \ | /
5200         3
5201 .ve
5202 
5203   Vertices are implicitly numbered consecutively 0,...,NVertices.
5204   Each rank owns a chunk of numVertices consecutive vertices.
5205   If numVertices is `PETSC_DECIDE`, PETSc will distribute them as evenly as possible using PetscLayout.
5206   If NVertices is `PETSC_DETERMINE` and numVertices is PETSC_DECIDE, NVertices is computed by PETSc as the maximum vertex index in cells + 1.
5207   If only NVertices is `PETSC_DETERMINE`, it is computed as the sum of numVertices over all ranks.
5208 
5209   The cell distribution is arbitrary non-overlapping, independent of the vertex distribution.
5210 
5211 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexBuildFromCellList()`, `DMPlexCreateFromCellListParallelPetsc()`, `DMPlexBuildCoordinatesFromCellListParallel()`,
5212           `PetscSF`
5213 @*/
5214 PetscErrorCode DMPlexBuildFromCellListParallel(DM dm, PetscInt numCells, PetscInt numVertices, PetscInt NVertices, PetscInt numCorners, const PetscInt cells[], PetscSF *vertexSF, PetscInt **verticesAdjSaved)
5215 {
5216   PetscSF     sfPoint;
5217   PetscLayout layout;
5218   PetscInt    numVerticesAdj, *verticesAdj, *cones, c, p;
5219 
5220   PetscFunctionBegin;
5221   PetscValidLogicalCollectiveInt(dm, NVertices, 4);
5222   PetscCall(PetscLogEventBegin(DMPLEX_BuildFromCellList, dm, 0, 0, 0));
5223   /* Get/check global number of vertices */
5224   {
5225     PetscInt       NVerticesInCells, i;
5226     const PetscInt len = numCells * numCorners;
5227 
5228     /* NVerticesInCells = max(cells) + 1 */
5229     NVerticesInCells = PETSC_MIN_INT;
5230     for (i = 0; i < len; i++)
5231       if (cells[i] > NVerticesInCells) NVerticesInCells = cells[i];
5232     ++NVerticesInCells;
5233     PetscCall(MPIU_Allreduce(MPI_IN_PLACE, &NVerticesInCells, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
5234 
5235     if (numVertices == PETSC_DECIDE && NVertices == PETSC_DECIDE) NVertices = NVerticesInCells;
5236     else
5237       PetscCheck(NVertices == PETSC_DECIDE || NVertices >= NVerticesInCells, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Specified global number of vertices %" PetscInt_FMT " must be greater than or equal to the number of vertices in cells %" PetscInt_FMT, NVertices, NVerticesInCells);
5238   }
5239   /* Count locally unique vertices */
5240   {
5241     PetscHSetI vhash;
5242     PetscInt   off = 0;
5243 
5244     PetscCall(PetscHSetICreate(&vhash));
5245     for (c = 0; c < numCells; ++c) {
5246       for (p = 0; p < numCorners; ++p) PetscCall(PetscHSetIAdd(vhash, cells[c * numCorners + p]));
5247     }
5248     PetscCall(PetscHSetIGetSize(vhash, &numVerticesAdj));
5249     if (!verticesAdjSaved) PetscCall(PetscMalloc1(numVerticesAdj, &verticesAdj));
5250     else verticesAdj = *verticesAdjSaved;
5251     PetscCall(PetscHSetIGetElems(vhash, &off, verticesAdj));
5252     PetscCall(PetscHSetIDestroy(&vhash));
5253     PetscCheck(off == numVerticesAdj, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Invalid number of local vertices %" PetscInt_FMT " should be %" PetscInt_FMT, off, numVerticesAdj);
5254   }
5255   PetscCall(PetscSortInt(numVerticesAdj, verticesAdj));
5256   /* Create cones */
5257   PetscCall(DMPlexSetChart(dm, 0, numCells + numVerticesAdj));
5258   for (c = 0; c < numCells; ++c) PetscCall(DMPlexSetConeSize(dm, c, numCorners));
5259   PetscCall(DMSetUp(dm));
5260   PetscCall(DMPlexGetCones(dm, &cones));
5261   for (c = 0; c < numCells; ++c) {
5262     for (p = 0; p < numCorners; ++p) {
5263       const PetscInt gv = cells[c * numCorners + p];
5264       PetscInt       lv;
5265 
5266       /* Positions within verticesAdj form 0-based local vertex numbering;
5267          we need to shift it by numCells to get correct DAG points (cells go first) */
5268       PetscCall(PetscFindInt(gv, numVerticesAdj, verticesAdj, &lv));
5269       PetscCheck(lv >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Could not find global vertex %" PetscInt_FMT " in local connectivity", gv);
5270       cones[c * numCorners + p] = lv + numCells;
5271     }
5272   }
5273   /* Build point sf */
5274   PetscCall(PetscLayoutCreate(PetscObjectComm((PetscObject)dm), &layout));
5275   PetscCall(PetscLayoutSetSize(layout, NVertices));
5276   PetscCall(PetscLayoutSetLocalSize(layout, numVertices));
5277   PetscCall(PetscLayoutSetBlockSize(layout, 1));
5278   PetscCall(PetscSFCreateByMatchingIndices(layout, numVerticesAdj, verticesAdj, NULL, numCells, numVerticesAdj, verticesAdj, NULL, numCells, vertexSF, &sfPoint));
5279   PetscCall(PetscLayoutDestroy(&layout));
5280   if (!verticesAdjSaved) PetscCall(PetscFree(verticesAdj));
5281   PetscCall(PetscObjectSetName((PetscObject)sfPoint, "point SF"));
5282   if (dm->sf) {
5283     const char *prefix;
5284 
5285     PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm->sf, &prefix));
5286     PetscCall(PetscObjectSetOptionsPrefix((PetscObject)sfPoint, prefix));
5287   }
5288   PetscCall(DMSetPointSF(dm, sfPoint));
5289   PetscCall(PetscSFDestroy(&sfPoint));
5290   if (vertexSF) PetscCall(PetscObjectSetName((PetscObject)*vertexSF, "Vertex Ownership SF"));
5291   /* Fill in the rest of the topology structure */
5292   PetscCall(DMPlexSymmetrize(dm));
5293   PetscCall(DMPlexStratify(dm));
5294   PetscCall(PetscLogEventEnd(DMPLEX_BuildFromCellList, dm, 0, 0, 0));
5295   PetscFunctionReturn(PETSC_SUCCESS);
5296 }
5297 
5298 /*@
5299   DMPlexBuildCoordinatesFromCellListParallel - Build `DM` coordinates from a list of coordinates for each owned vertex (common mesh generator output)
5300 
5301   Collective; No Fortran Support
5302 
5303   Input Parameters:
5304 + dm           - The `DM`
5305 . spaceDim     - The spatial dimension used for coordinates
5306 . sfVert       - `PetscSF` describing complete vertex ownership
5307 - vertexCoords - An array of numVertices*spaceDim numbers, the coordinates of each vertex
5308 
5309   Level: advanced
5310 
5311 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexBuildCoordinatesFromCellList()`, `DMPlexCreateFromCellListParallelPetsc()`, `DMPlexBuildFromCellListParallel()`
5312 @*/
5313 PetscErrorCode DMPlexBuildCoordinatesFromCellListParallel(DM dm, PetscInt spaceDim, PetscSF sfVert, const PetscReal vertexCoords[])
5314 {
5315   PetscSection coordSection;
5316   Vec          coordinates;
5317   PetscScalar *coords;
5318   PetscInt     numVertices, numVerticesAdj, coordSize, v, vStart, vEnd;
5319 
5320   PetscFunctionBegin;
5321   PetscCall(PetscLogEventBegin(DMPLEX_BuildCoordinatesFromCellList, dm, 0, 0, 0));
5322   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
5323   PetscCheck(vStart >= 0 && vEnd >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "DM is not set up properly. DMPlexBuildFromCellList() should be called first.");
5324   PetscCall(DMSetCoordinateDim(dm, spaceDim));
5325   PetscCall(PetscSFGetGraph(sfVert, &numVertices, &numVerticesAdj, NULL, NULL));
5326   PetscCheck(vEnd - vStart == numVerticesAdj, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Supplied sfVert has wrong number of leaves = %" PetscInt_FMT " != %" PetscInt_FMT " = vEnd - vStart", numVerticesAdj, vEnd - vStart);
5327   PetscCall(DMGetCoordinateSection(dm, &coordSection));
5328   PetscCall(PetscSectionSetNumFields(coordSection, 1));
5329   PetscCall(PetscSectionSetFieldComponents(coordSection, 0, spaceDim));
5330   PetscCall(PetscSectionSetChart(coordSection, vStart, vEnd));
5331   for (v = vStart; v < vEnd; ++v) {
5332     PetscCall(PetscSectionSetDof(coordSection, v, spaceDim));
5333     PetscCall(PetscSectionSetFieldDof(coordSection, v, 0, spaceDim));
5334   }
5335   PetscCall(PetscSectionSetUp(coordSection));
5336   PetscCall(PetscSectionGetStorageSize(coordSection, &coordSize));
5337   PetscCall(VecCreate(PetscObjectComm((PetscObject)dm), &coordinates));
5338   PetscCall(VecSetBlockSize(coordinates, spaceDim));
5339   PetscCall(PetscObjectSetName((PetscObject)coordinates, "coordinates"));
5340   PetscCall(VecSetSizes(coordinates, coordSize, PETSC_DETERMINE));
5341   PetscCall(VecSetType(coordinates, VECSTANDARD));
5342   PetscCall(VecGetArray(coordinates, &coords));
5343   {
5344     MPI_Datatype coordtype;
5345 
5346     /* Need a temp buffer for coords if we have complex/single */
5347     PetscCallMPI(MPI_Type_contiguous(spaceDim, MPIU_SCALAR, &coordtype));
5348     PetscCallMPI(MPI_Type_commit(&coordtype));
5349 #if defined(PETSC_USE_COMPLEX)
5350     {
5351       PetscScalar *svertexCoords;
5352       PetscInt     i;
5353       PetscCall(PetscMalloc1(numVertices * spaceDim, &svertexCoords));
5354       for (i = 0; i < numVertices * spaceDim; i++) svertexCoords[i] = vertexCoords[i];
5355       PetscCall(PetscSFBcastBegin(sfVert, coordtype, svertexCoords, coords, MPI_REPLACE));
5356       PetscCall(PetscSFBcastEnd(sfVert, coordtype, svertexCoords, coords, MPI_REPLACE));
5357       PetscCall(PetscFree(svertexCoords));
5358     }
5359 #else
5360     PetscCall(PetscSFBcastBegin(sfVert, coordtype, vertexCoords, coords, MPI_REPLACE));
5361     PetscCall(PetscSFBcastEnd(sfVert, coordtype, vertexCoords, coords, MPI_REPLACE));
5362 #endif
5363     PetscCallMPI(MPI_Type_free(&coordtype));
5364   }
5365   PetscCall(VecRestoreArray(coordinates, &coords));
5366   PetscCall(DMSetCoordinatesLocal(dm, coordinates));
5367   PetscCall(VecDestroy(&coordinates));
5368   PetscCall(PetscLogEventEnd(DMPLEX_BuildCoordinatesFromCellList, dm, 0, 0, 0));
5369   PetscFunctionReturn(PETSC_SUCCESS);
5370 }
5371 
5372 /*@
5373   DMPlexCreateFromCellListParallelPetsc - Create distributed `DMPLEX` from a list of vertices for each cell (common mesh generator output)
5374 
5375   Collective
5376 
5377   Input Parameters:
5378 + comm         - The communicator
5379 . dim          - The topological dimension of the mesh
5380 . numCells     - The number of cells owned by this process
5381 . numVertices  - The number of vertices owned by this process, or `PETSC_DECIDE`
5382 . NVertices    - The global number of vertices, or `PETSC_DECIDE`
5383 . numCorners   - The number of vertices for each cell
5384 . interpolate  - Flag indicating that intermediate mesh entities (faces, edges) should be created automatically
5385 . cells        - An array of numCells*numCorners numbers, the global vertex numbers for each cell
5386 . spaceDim     - The spatial dimension used for coordinates
5387 - vertexCoords - An array of numVertices*spaceDim numbers, the coordinates of each vertex
5388 
5389   Output Parameters:
5390 + dm          - The `DM`
5391 . vertexSF    - (Optional) `PetscSF` describing complete vertex ownership
5392 - verticesAdj - (Optional) vertex adjacency array
5393 
5394   Level: intermediate
5395 
5396   Notes:
5397   This function is just a convenient sequence of `DMCreate()`, `DMSetType()`, `DMSetDimension()`,
5398   `DMPlexBuildFromCellListParallel()`, `DMPlexInterpolate()`, `DMPlexBuildCoordinatesFromCellListParallel()`
5399 
5400   See `DMPlexBuildFromCellListParallel()` for an example and details about the topology-related parameters.
5401 
5402   See `DMPlexBuildCoordinatesFromCellListParallel()` for details about the geometry-related parameters.
5403 
5404 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateFromCellListPetsc()`, `DMPlexBuildFromCellListParallel()`, `DMPlexBuildCoordinatesFromCellListParallel()`, `DMPlexCreateFromDAG()`, `DMPlexCreate()`
5405 @*/
5406 PetscErrorCode DMPlexCreateFromCellListParallelPetsc(MPI_Comm comm, PetscInt dim, PetscInt numCells, PetscInt numVertices, PetscInt NVertices, PetscInt numCorners, PetscBool interpolate, const PetscInt cells[], PetscInt spaceDim, const PetscReal vertexCoords[], PetscSF *vertexSF, PetscInt **verticesAdj, DM *dm)
5407 {
5408   PetscSF sfVert;
5409 
5410   PetscFunctionBegin;
5411   PetscCall(DMCreate(comm, dm));
5412   PetscCall(DMSetType(*dm, DMPLEX));
5413   PetscValidLogicalCollectiveInt(*dm, dim, 2);
5414   PetscValidLogicalCollectiveInt(*dm, spaceDim, 9);
5415   PetscCall(DMSetDimension(*dm, dim));
5416   PetscCall(DMPlexBuildFromCellListParallel(*dm, numCells, numVertices, NVertices, numCorners, cells, &sfVert, verticesAdj));
5417   if (interpolate) {
5418     DM idm;
5419 
5420     PetscCall(DMPlexInterpolate(*dm, &idm));
5421     PetscCall(DMDestroy(dm));
5422     *dm = idm;
5423   }
5424   PetscCall(DMPlexBuildCoordinatesFromCellListParallel(*dm, spaceDim, sfVert, vertexCoords));
5425   if (vertexSF) *vertexSF = sfVert;
5426   else PetscCall(PetscSFDestroy(&sfVert));
5427   PetscFunctionReturn(PETSC_SUCCESS);
5428 }
5429 
5430 /*@
5431   DMPlexBuildFromCellList - Build `DMPLEX` topology from a list of vertices for each cell (common mesh generator output)
5432 
5433   Collective; No Fortran Support
5434 
5435   Input Parameters:
5436 + dm          - The `DM`
5437 . numCells    - The number of cells owned by this process
5438 . numVertices - The number of vertices owned by this process, or `PETSC_DETERMINE`
5439 . numCorners  - The number of vertices for each cell
5440 - cells       - An array of `numCells` x `numCorners` numbers, the global vertex numbers for each cell
5441 
5442   Level: advanced
5443 
5444   Notes:
5445   Two triangles sharing a face
5446 .vb
5447 
5448         2
5449       / | \
5450      /  |  \
5451     /   |   \
5452    0  0 | 1  3
5453     \   |   /
5454      \  |  /
5455       \ | /
5456         1
5457 .ve
5458   would have input
5459 .vb
5460   numCells = 2, numVertices = 4
5461   cells = [0 1 2  1 3 2]
5462 .ve
5463   which would result in the `DMPLEX`
5464 .vb
5465 
5466         4
5467       / | \
5468      /  |  \
5469     /   |   \
5470    2  0 | 1  5
5471     \   |   /
5472      \  |  /
5473       \ | /
5474         3
5475 .ve
5476 
5477   If numVertices is `PETSC_DETERMINE`, it is computed by PETSc as the maximum vertex index in cells + 1.
5478 
5479 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexBuildFromCellListParallel()`, `DMPlexBuildCoordinatesFromCellList()`, `DMPlexCreateFromCellListPetsc()`
5480 @*/
5481 PetscErrorCode DMPlexBuildFromCellList(DM dm, PetscInt numCells, PetscInt numVertices, PetscInt numCorners, const PetscInt cells[])
5482 {
5483   PetscInt *cones, c, p, dim;
5484 
5485   PetscFunctionBegin;
5486   PetscCall(PetscLogEventBegin(DMPLEX_BuildFromCellList, dm, 0, 0, 0));
5487   PetscCall(DMGetDimension(dm, &dim));
5488   /* Get/check global number of vertices */
5489   {
5490     PetscInt       NVerticesInCells, i;
5491     const PetscInt len = numCells * numCorners;
5492 
5493     /* NVerticesInCells = max(cells) + 1 */
5494     NVerticesInCells = PETSC_MIN_INT;
5495     for (i = 0; i < len; i++)
5496       if (cells[i] > NVerticesInCells) NVerticesInCells = cells[i];
5497     ++NVerticesInCells;
5498 
5499     if (numVertices == PETSC_DECIDE) numVertices = NVerticesInCells;
5500     else
5501       PetscCheck(numVertices >= NVerticesInCells, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Specified number of vertices %" PetscInt_FMT " must be greater than or equal to the number of vertices in cells %" PetscInt_FMT, numVertices, NVerticesInCells);
5502   }
5503   PetscCall(DMPlexSetChart(dm, 0, numCells + numVertices));
5504   for (c = 0; c < numCells; ++c) PetscCall(DMPlexSetConeSize(dm, c, numCorners));
5505   PetscCall(DMSetUp(dm));
5506   PetscCall(DMPlexGetCones(dm, &cones));
5507   for (c = 0; c < numCells; ++c) {
5508     for (p = 0; p < numCorners; ++p) cones[c * numCorners + p] = cells[c * numCorners + p] + numCells;
5509   }
5510   PetscCall(DMPlexSymmetrize(dm));
5511   PetscCall(DMPlexStratify(dm));
5512   PetscCall(PetscLogEventEnd(DMPLEX_BuildFromCellList, dm, 0, 0, 0));
5513   PetscFunctionReturn(PETSC_SUCCESS);
5514 }
5515 
5516 /*@
5517   DMPlexBuildCoordinatesFromCellList - Build `DM` coordinates from a list of coordinates for each owned vertex (common mesh generator output)
5518 
5519   Collective
5520 
5521   Input Parameters:
5522 + dm           - The `DM`
5523 . spaceDim     - The spatial dimension used for coordinates
5524 - vertexCoords - An array of numVertices*spaceDim numbers, the coordinates of each vertex
5525 
5526   Level: advanced
5527 
5528 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexBuildCoordinatesFromCellListParallel()`, `DMPlexCreateFromCellListPetsc()`, `DMPlexBuildFromCellList()`
5529 @*/
5530 PetscErrorCode DMPlexBuildCoordinatesFromCellList(DM dm, PetscInt spaceDim, const PetscReal vertexCoords[])
5531 {
5532   PetscSection coordSection;
5533   Vec          coordinates;
5534   DM           cdm;
5535   PetscScalar *coords;
5536   PetscInt     v, vStart, vEnd, d;
5537 
5538   PetscFunctionBegin;
5539   PetscCall(PetscLogEventBegin(DMPLEX_BuildCoordinatesFromCellList, dm, 0, 0, 0));
5540   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
5541   PetscCheck(vStart >= 0 && vEnd >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "DM is not set up properly. DMPlexBuildFromCellList() should be called first.");
5542   PetscCall(DMSetCoordinateDim(dm, spaceDim));
5543   PetscCall(DMGetCoordinateSection(dm, &coordSection));
5544   PetscCall(PetscSectionSetNumFields(coordSection, 1));
5545   PetscCall(PetscSectionSetFieldComponents(coordSection, 0, spaceDim));
5546   PetscCall(PetscSectionSetChart(coordSection, vStart, vEnd));
5547   for (v = vStart; v < vEnd; ++v) {
5548     PetscCall(PetscSectionSetDof(coordSection, v, spaceDim));
5549     PetscCall(PetscSectionSetFieldDof(coordSection, v, 0, spaceDim));
5550   }
5551   PetscCall(PetscSectionSetUp(coordSection));
5552 
5553   PetscCall(DMGetCoordinateDM(dm, &cdm));
5554   PetscCall(DMCreateLocalVector(cdm, &coordinates));
5555   PetscCall(VecSetBlockSize(coordinates, spaceDim));
5556   PetscCall(PetscObjectSetName((PetscObject)coordinates, "coordinates"));
5557   PetscCall(VecGetArrayWrite(coordinates, &coords));
5558   for (v = 0; v < vEnd - vStart; ++v) {
5559     for (d = 0; d < spaceDim; ++d) coords[v * spaceDim + d] = vertexCoords[v * spaceDim + d];
5560   }
5561   PetscCall(VecRestoreArrayWrite(coordinates, &coords));
5562   PetscCall(DMSetCoordinatesLocal(dm, coordinates));
5563   PetscCall(VecDestroy(&coordinates));
5564   PetscCall(PetscLogEventEnd(DMPLEX_BuildCoordinatesFromCellList, dm, 0, 0, 0));
5565   PetscFunctionReturn(PETSC_SUCCESS);
5566 }
5567 
5568 /*@
5569   DMPlexCreateFromCellListPetsc - Create `DMPLEX` from a list of vertices for each cell (common mesh generator output), but only process 0 takes in the input
5570 
5571   Collective
5572 
5573   Input Parameters:
5574 + comm         - The communicator
5575 . dim          - The topological dimension of the mesh
5576 . numCells     - The number of cells, only on process 0
5577 . numVertices  - The number of vertices owned by this process, or `PETSC_DECIDE`, only on process 0
5578 . numCorners   - The number of vertices for each cell, only on process 0
5579 . interpolate  - Flag indicating that intermediate mesh entities (faces, edges) should be created automatically
5580 . cells        - An array of numCells*numCorners numbers, the vertices for each cell, only on process 0
5581 . spaceDim     - The spatial dimension used for coordinates
5582 - vertexCoords - An array of numVertices*spaceDim numbers, the coordinates of each vertex, only on process 0
5583 
5584   Output Parameter:
5585 . dm - The `DM`, which only has points on process 0
5586 
5587   Level: intermediate
5588 
5589   Notes:
5590   This function is just a convenient sequence of `DMCreate()`, `DMSetType()`, `DMSetDimension()`, `DMPlexBuildFromCellList()`,
5591   `DMPlexInterpolate()`, `DMPlexBuildCoordinatesFromCellList()`
5592 
5593   See `DMPlexBuildFromCellList()` for an example and details about the topology-related parameters.
5594   See `DMPlexBuildCoordinatesFromCellList()` for details about the geometry-related parameters.
5595   See `DMPlexCreateFromCellListParallelPetsc()` for parallel input
5596 
5597 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateFromCellListParallelPetsc()`, `DMPlexBuildFromCellList()`, `DMPlexBuildCoordinatesFromCellList()`, `DMPlexCreateFromDAG()`, `DMPlexCreate()`
5598 @*/
5599 PetscErrorCode DMPlexCreateFromCellListPetsc(MPI_Comm comm, PetscInt dim, PetscInt numCells, PetscInt numVertices, PetscInt numCorners, PetscBool interpolate, const PetscInt cells[], PetscInt spaceDim, const PetscReal vertexCoords[], DM *dm)
5600 {
5601   PetscMPIInt rank;
5602 
5603   PetscFunctionBegin;
5604   PetscCheck(dim, comm, PETSC_ERR_ARG_OUTOFRANGE, "This is not appropriate for 0-dimensional meshes. Consider either creating the DM using DMPlexCreateFromDAG(), by hand, or using DMSwarm.");
5605   PetscCallMPI(MPI_Comm_rank(comm, &rank));
5606   PetscCall(DMCreate(comm, dm));
5607   PetscCall(DMSetType(*dm, DMPLEX));
5608   PetscCall(DMSetDimension(*dm, dim));
5609   if (rank == 0) PetscCall(DMPlexBuildFromCellList(*dm, numCells, numVertices, numCorners, cells));
5610   else PetscCall(DMPlexBuildFromCellList(*dm, 0, 0, 0, NULL));
5611   if (interpolate) {
5612     DM idm;
5613 
5614     PetscCall(DMPlexInterpolate(*dm, &idm));
5615     PetscCall(DMDestroy(dm));
5616     *dm = idm;
5617   }
5618   if (rank == 0) PetscCall(DMPlexBuildCoordinatesFromCellList(*dm, spaceDim, vertexCoords));
5619   else PetscCall(DMPlexBuildCoordinatesFromCellList(*dm, spaceDim, NULL));
5620   PetscFunctionReturn(PETSC_SUCCESS);
5621 }
5622 
5623 /*@
5624   DMPlexCreateFromDAG - This takes as input the adjacency-list representation of the Directed Acyclic Graph (Hasse Diagram) encoding a mesh, and produces a `DM`
5625 
5626   Input Parameters:
5627 + dm               - The empty `DM` object, usually from `DMCreate()` and `DMSetDimension()`
5628 . depth            - The depth of the DAG
5629 . numPoints        - Array of size depth + 1 containing the number of points at each `depth`
5630 . coneSize         - The cone size of each point
5631 . cones            - The concatenation of the cone points for each point, the cone list must be oriented correctly for each point
5632 . coneOrientations - The orientation of each cone point
5633 - vertexCoords     - An array of `numPoints`[0]*spacedim numbers representing the coordinates of each vertex, with spacedim the value set via `DMSetCoordinateDim()`
5634 
5635   Output Parameter:
5636 . dm - The `DM`
5637 
5638   Level: advanced
5639 
5640   Note:
5641   Two triangles sharing a face would have input
5642 .vb
5643   depth = 1, numPoints = [4 2], coneSize = [3 3 0 0 0 0]
5644   cones = [2 3 4  3 5 4], coneOrientations = [0 0 0  0 0 0]
5645  vertexCoords = [-1.0 0.0  0.0 -1.0  0.0 1.0  1.0 0.0]
5646 .ve
5647   which would result in the DMPlex
5648 .vb
5649         4
5650       / | \
5651      /  |  \
5652     /   |   \
5653    2  0 | 1  5
5654     \   |   /
5655      \  |  /
5656       \ | /
5657         3
5658 .ve
5659   Notice that all points are numbered consecutively, unlike `DMPlexCreateFromCellListPetsc()`
5660 
5661 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateFromCellListPetsc()`, `DMPlexCreate()`
5662 @*/
5663 PetscErrorCode DMPlexCreateFromDAG(DM dm, PetscInt depth, const PetscInt numPoints[], const PetscInt coneSize[], const PetscInt cones[], const PetscInt coneOrientations[], const PetscScalar vertexCoords[])
5664 {
5665   Vec          coordinates;
5666   PetscSection coordSection;
5667   PetscScalar *coords;
5668   PetscInt     coordSize, firstVertex = -1, pStart = 0, pEnd = 0, p, v, dim, dimEmbed, d, off;
5669 
5670   PetscFunctionBegin;
5671   PetscCall(DMGetDimension(dm, &dim));
5672   PetscCall(DMGetCoordinateDim(dm, &dimEmbed));
5673   PetscCheck(dimEmbed >= dim, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Embedding dimension %" PetscInt_FMT " cannot be less than intrinsic dimension %" PetscInt_FMT, dimEmbed, dim);
5674   for (d = 0; d <= depth; ++d) pEnd += numPoints[d];
5675   PetscCall(DMPlexSetChart(dm, pStart, pEnd));
5676   for (p = pStart; p < pEnd; ++p) {
5677     PetscCall(DMPlexSetConeSize(dm, p, coneSize[p - pStart]));
5678     if (firstVertex < 0 && !coneSize[p - pStart]) firstVertex = p - pStart;
5679   }
5680   PetscCheck(firstVertex >= 0 || !numPoints[0], PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Expected %" PetscInt_FMT " vertices but could not find any", numPoints[0]);
5681   PetscCall(DMSetUp(dm)); /* Allocate space for cones */
5682   for (p = pStart, off = 0; p < pEnd; off += coneSize[p - pStart], ++p) {
5683     PetscCall(DMPlexSetCone(dm, p, &cones[off]));
5684     PetscCall(DMPlexSetConeOrientation(dm, p, &coneOrientations[off]));
5685   }
5686   PetscCall(DMPlexSymmetrize(dm));
5687   PetscCall(DMPlexStratify(dm));
5688   /* Build coordinates */
5689   PetscCall(DMGetCoordinateSection(dm, &coordSection));
5690   PetscCall(PetscSectionSetNumFields(coordSection, 1));
5691   PetscCall(PetscSectionSetFieldComponents(coordSection, 0, dimEmbed));
5692   PetscCall(PetscSectionSetChart(coordSection, firstVertex, firstVertex + numPoints[0]));
5693   for (v = firstVertex; v < firstVertex + numPoints[0]; ++v) {
5694     PetscCall(PetscSectionSetDof(coordSection, v, dimEmbed));
5695     PetscCall(PetscSectionSetFieldDof(coordSection, v, 0, dimEmbed));
5696   }
5697   PetscCall(PetscSectionSetUp(coordSection));
5698   PetscCall(PetscSectionGetStorageSize(coordSection, &coordSize));
5699   PetscCall(VecCreate(PETSC_COMM_SELF, &coordinates));
5700   PetscCall(PetscObjectSetName((PetscObject)coordinates, "coordinates"));
5701   PetscCall(VecSetSizes(coordinates, coordSize, PETSC_DETERMINE));
5702   PetscCall(VecSetBlockSize(coordinates, dimEmbed));
5703   PetscCall(VecSetType(coordinates, VECSTANDARD));
5704   if (vertexCoords) {
5705     PetscCall(VecGetArray(coordinates, &coords));
5706     for (v = 0; v < numPoints[0]; ++v) {
5707       PetscInt off;
5708 
5709       PetscCall(PetscSectionGetOffset(coordSection, v + firstVertex, &off));
5710       for (d = 0; d < dimEmbed; ++d) coords[off + d] = vertexCoords[v * dimEmbed + d];
5711     }
5712   }
5713   PetscCall(VecRestoreArray(coordinates, &coords));
5714   PetscCall(DMSetCoordinatesLocal(dm, coordinates));
5715   PetscCall(VecDestroy(&coordinates));
5716   PetscFunctionReturn(PETSC_SUCCESS);
5717 }
5718 
5719 /*
5720   DMPlexCreateCellVertexFromFile - Create a `DMPLEX` mesh from a simple cell-vertex file.
5721 
5722   Collective
5723 
5724 + comm        - The MPI communicator
5725 . filename    - Name of the .dat file
5726 - interpolate - Create faces and edges in the mesh
5727 
5728   Output Parameter:
5729 . dm  - The `DM` object representing the mesh
5730 
5731   Level: beginner
5732 
5733   Note:
5734   The format is the simplest possible:
5735 .vb
5736   dim Ne Nv Nc Nl
5737   v_1 v_2 ... v_Nc
5738   ...
5739   x y z marker_1 ... marker_Nl
5740 .ve
5741 
5742   Developer Note:
5743   Should use a `PetscViewer` not a filename
5744 
5745 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateFromFile()`, `DMPlexCreateGmsh()`, `DMPlexCreate()`
5746 */
5747 static PetscErrorCode DMPlexCreateCellVertexFromFile(MPI_Comm comm, const char filename[], PetscBool interpolate, DM *dm)
5748 {
5749   DMLabel      marker;
5750   PetscViewer  viewer;
5751   Vec          coordinates;
5752   PetscSection coordSection;
5753   PetscScalar *coords;
5754   char         line[PETSC_MAX_PATH_LEN];
5755   PetscInt     cdim, coordSize, v, c, d;
5756   PetscMPIInt  rank;
5757   int          snum, dim, Nv, Nc, Ncn, Nl;
5758 
5759   PetscFunctionBegin;
5760   PetscCallMPI(MPI_Comm_rank(comm, &rank));
5761   PetscCall(PetscViewerCreate(comm, &viewer));
5762   PetscCall(PetscViewerSetType(viewer, PETSCVIEWERASCII));
5763   PetscCall(PetscViewerFileSetMode(viewer, FILE_MODE_READ));
5764   PetscCall(PetscViewerFileSetName(viewer, filename));
5765   if (rank == 0) {
5766     PetscCall(PetscViewerRead(viewer, line, 5, NULL, PETSC_STRING));
5767     snum = sscanf(line, "%d %d %d %d %d", &dim, &Nc, &Nv, &Ncn, &Nl);
5768     PetscCheck(snum == 5, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unable to parse cell-vertex file: %s", line);
5769   } else {
5770     Nc = Nv = Ncn = Nl = 0;
5771   }
5772   PetscCallMPI(MPI_Bcast(&dim, 1, MPI_INT, 0, comm));
5773   cdim = (PetscInt)dim;
5774   PetscCall(DMCreate(comm, dm));
5775   PetscCall(DMSetType(*dm, DMPLEX));
5776   PetscCall(DMPlexSetChart(*dm, 0, Nc + Nv));
5777   PetscCall(DMSetDimension(*dm, (PetscInt)dim));
5778   PetscCall(DMSetCoordinateDim(*dm, cdim));
5779   /* Read topology */
5780   if (rank == 0) {
5781     char     format[PETSC_MAX_PATH_LEN];
5782     PetscInt cone[8];
5783     int      vbuf[8], v;
5784 
5785     for (c = 0; c < Ncn; ++c) {
5786       format[c * 3 + 0] = '%';
5787       format[c * 3 + 1] = 'd';
5788       format[c * 3 + 2] = ' ';
5789     }
5790     format[Ncn * 3 - 1] = '\0';
5791     for (c = 0; c < Nc; ++c) PetscCall(DMPlexSetConeSize(*dm, c, Ncn));
5792     PetscCall(DMSetUp(*dm));
5793     for (c = 0; c < Nc; ++c) {
5794       PetscCall(PetscViewerRead(viewer, line, Ncn, NULL, PETSC_STRING));
5795       switch (Ncn) {
5796       case 2:
5797         snum = sscanf(line, format, &vbuf[0], &vbuf[1]);
5798         break;
5799       case 3:
5800         snum = sscanf(line, format, &vbuf[0], &vbuf[1], &vbuf[2]);
5801         break;
5802       case 4:
5803         snum = sscanf(line, format, &vbuf[0], &vbuf[1], &vbuf[2], &vbuf[3]);
5804         break;
5805       case 6:
5806         snum = sscanf(line, format, &vbuf[0], &vbuf[1], &vbuf[2], &vbuf[3], &vbuf[4], &vbuf[5]);
5807         break;
5808       case 8:
5809         snum = sscanf(line, format, &vbuf[0], &vbuf[1], &vbuf[2], &vbuf[3], &vbuf[4], &vbuf[5], &vbuf[6], &vbuf[7]);
5810         break;
5811       default:
5812         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "No cell shape with %d vertices", Ncn);
5813       }
5814       PetscCheck(snum == Ncn, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unable to parse cell-vertex file: %s", line);
5815       for (v = 0; v < Ncn; ++v) cone[v] = vbuf[v] + Nc;
5816       /* Hexahedra are inverted */
5817       if (Ncn == 8) {
5818         PetscInt tmp = cone[1];
5819         cone[1]      = cone[3];
5820         cone[3]      = tmp;
5821       }
5822       PetscCall(DMPlexSetCone(*dm, c, cone));
5823     }
5824   }
5825   PetscCall(DMPlexSymmetrize(*dm));
5826   PetscCall(DMPlexStratify(*dm));
5827   /* Read coordinates */
5828   PetscCall(DMGetCoordinateSection(*dm, &coordSection));
5829   PetscCall(PetscSectionSetNumFields(coordSection, 1));
5830   PetscCall(PetscSectionSetFieldComponents(coordSection, 0, cdim));
5831   PetscCall(PetscSectionSetChart(coordSection, Nc, Nc + Nv));
5832   for (v = Nc; v < Nc + Nv; ++v) {
5833     PetscCall(PetscSectionSetDof(coordSection, v, cdim));
5834     PetscCall(PetscSectionSetFieldDof(coordSection, v, 0, cdim));
5835   }
5836   PetscCall(PetscSectionSetUp(coordSection));
5837   PetscCall(PetscSectionGetStorageSize(coordSection, &coordSize));
5838   PetscCall(VecCreate(PETSC_COMM_SELF, &coordinates));
5839   PetscCall(PetscObjectSetName((PetscObject)coordinates, "coordinates"));
5840   PetscCall(VecSetSizes(coordinates, coordSize, PETSC_DETERMINE));
5841   PetscCall(VecSetBlockSize(coordinates, cdim));
5842   PetscCall(VecSetType(coordinates, VECSTANDARD));
5843   PetscCall(VecGetArray(coordinates, &coords));
5844   if (rank == 0) {
5845     char   format[PETSC_MAX_PATH_LEN];
5846     double x[3];
5847     int    l, val[3];
5848 
5849     if (Nl) {
5850       for (l = 0; l < Nl; ++l) {
5851         format[l * 3 + 0] = '%';
5852         format[l * 3 + 1] = 'd';
5853         format[l * 3 + 2] = ' ';
5854       }
5855       format[Nl * 3 - 1] = '\0';
5856       PetscCall(DMCreateLabel(*dm, "marker"));
5857       PetscCall(DMGetLabel(*dm, "marker", &marker));
5858     }
5859     for (v = 0; v < Nv; ++v) {
5860       PetscCall(PetscViewerRead(viewer, line, 3 + Nl, NULL, PETSC_STRING));
5861       snum = sscanf(line, "%lg %lg %lg", &x[0], &x[1], &x[2]);
5862       PetscCheck(snum == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unable to parse cell-vertex file: %s", line);
5863       switch (Nl) {
5864       case 0:
5865         snum = 0;
5866         break;
5867       case 1:
5868         snum = sscanf(line, format, &val[0]);
5869         break;
5870       case 2:
5871         snum = sscanf(line, format, &val[0], &val[1]);
5872         break;
5873       case 3:
5874         snum = sscanf(line, format, &val[0], &val[1], &val[2]);
5875         break;
5876       default:
5877         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Request support for %d labels", Nl);
5878       }
5879       PetscCheck(snum == Nl, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unable to parse cell-vertex file: %s", line);
5880       for (d = 0; d < cdim; ++d) coords[v * cdim + d] = x[d];
5881       for (l = 0; l < Nl; ++l) PetscCall(DMLabelSetValue(marker, v + Nc, val[l]));
5882     }
5883   }
5884   PetscCall(VecRestoreArray(coordinates, &coords));
5885   PetscCall(DMSetCoordinatesLocal(*dm, coordinates));
5886   PetscCall(VecDestroy(&coordinates));
5887   PetscCall(PetscViewerDestroy(&viewer));
5888   if (interpolate) {
5889     DM      idm;
5890     DMLabel bdlabel;
5891 
5892     PetscCall(DMPlexInterpolate(*dm, &idm));
5893     PetscCall(DMDestroy(dm));
5894     *dm = idm;
5895 
5896     if (!Nl) {
5897       PetscCall(DMCreateLabel(*dm, "marker"));
5898       PetscCall(DMGetLabel(*dm, "marker", &bdlabel));
5899       PetscCall(DMPlexMarkBoundaryFaces(*dm, PETSC_DETERMINE, bdlabel));
5900       PetscCall(DMPlexLabelComplete(*dm, bdlabel));
5901     }
5902   }
5903   PetscFunctionReturn(PETSC_SUCCESS);
5904 }
5905 
5906 /*@
5907   DMPlexCreateFromFile - This takes a filename and produces a `DM`
5908 
5909   Collective
5910 
5911   Input Parameters:
5912 + comm        - The communicator
5913 . filename    - A file name
5914 . plexname    - The object name of the resulting `DM`, also used for intra-datafile lookup by some formats
5915 - interpolate - Flag to create intermediate mesh pieces (edges, faces)
5916 
5917   Output Parameter:
5918 . dm - The `DM`
5919 
5920   Options Database Key:
5921 . -dm_plex_create_from_hdf5_xdmf - use the `PETSC_VIEWER_HDF5_XDMF` format for reading HDF5
5922 
5923   Use `-dm_plex_create_ prefix` to pass options to the internal `PetscViewer`, e.g.
5924 $ -dm_plex_create_viewer_hdf5_collective
5925 
5926   Level: beginner
5927 
5928   Notes:
5929   Using `PETSCVIEWERHDF5` type with `PETSC_VIEWER_HDF5_PETSC` format, one can save multiple `DMPLEX`
5930   meshes in a single HDF5 file. This in turn requires one to name the `DMPLEX` object with `PetscObjectSetName()`
5931   before saving it with `DMView()` and before loading it with `DMLoad()` for identification of the mesh object.
5932   The input parameter name is thus used to name the `DMPLEX` object when `DMPlexCreateFromFile()` internally
5933   calls `DMLoad()`. Currently, name is ignored for other viewer types and/or formats.
5934 
5935 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateFromDAG()`, `DMPlexCreateFromCellListPetsc()`, `DMPlexCreate()`, `PetscObjectSetName()`, `DMView()`, `DMLoad()`
5936 @*/
5937 PetscErrorCode DMPlexCreateFromFile(MPI_Comm comm, const char filename[], const char plexname[], PetscBool interpolate, DM *dm)
5938 {
5939   const char  extGmsh[]      = ".msh";
5940   const char  extGmsh2[]     = ".msh2";
5941   const char  extGmsh4[]     = ".msh4";
5942   const char  extCGNS[]      = ".cgns";
5943   const char  extExodus[]    = ".exo";
5944   const char  extExodus_e[]  = ".e";
5945   const char  extGenesis[]   = ".gen";
5946   const char  extFluent[]    = ".cas";
5947   const char  extHDF5[]      = ".h5";
5948   const char  extXDMFHDF5[]  = ".xdmf.h5";
5949   const char  extPLY[]       = ".ply";
5950   const char  extEGADSLite[] = ".egadslite";
5951   const char  extEGADS[]     = ".egads";
5952   const char  extIGES[]      = ".igs";
5953   const char  extSTEP[]      = ".stp";
5954   const char  extCV[]        = ".dat";
5955   size_t      len;
5956   PetscBool   isGmsh, isGmsh2, isGmsh4, isCGNS, isExodus, isGenesis, isFluent, isHDF5, isPLY, isEGADSLite, isEGADS, isIGES, isSTEP, isCV, isXDMFHDF5;
5957   PetscMPIInt rank;
5958 
5959   PetscFunctionBegin;
5960   PetscAssertPointer(filename, 2);
5961   if (plexname) PetscAssertPointer(plexname, 3);
5962   PetscAssertPointer(dm, 5);
5963   PetscCall(DMInitializePackage());
5964   PetscCall(PetscLogEventBegin(DMPLEX_CreateFromFile, 0, 0, 0, 0));
5965   PetscCallMPI(MPI_Comm_rank(comm, &rank));
5966   PetscCall(PetscStrlen(filename, &len));
5967   PetscCheck(len, comm, PETSC_ERR_ARG_WRONG, "Filename must be a valid path");
5968 
5969 #define CheckExtension(extension__, is_extension__) \
5970   do { \
5971     PetscAssert(sizeof(extension__), comm, PETSC_ERR_PLIB, "Zero-size extension: %s", extension__); \
5972     /* don't count the null-terminator at the end */ \
5973     const size_t ext_len = sizeof(extension__) - 1; \
5974     if (len < ext_len) { \
5975       is_extension__ = PETSC_FALSE; \
5976     } else { \
5977       PetscCall(PetscStrncmp(filename + len - ext_len, extension__, ext_len, &is_extension__)); \
5978     } \
5979   } while (0)
5980 
5981   CheckExtension(extGmsh, isGmsh);
5982   CheckExtension(extGmsh2, isGmsh2);
5983   CheckExtension(extGmsh4, isGmsh4);
5984   CheckExtension(extCGNS, isCGNS);
5985   CheckExtension(extExodus, isExodus);
5986   if (!isExodus) CheckExtension(extExodus_e, isExodus);
5987   CheckExtension(extGenesis, isGenesis);
5988   CheckExtension(extFluent, isFluent);
5989   CheckExtension(extHDF5, isHDF5);
5990   CheckExtension(extPLY, isPLY);
5991   CheckExtension(extEGADSLite, isEGADSLite);
5992   CheckExtension(extEGADS, isEGADS);
5993   CheckExtension(extIGES, isIGES);
5994   CheckExtension(extSTEP, isSTEP);
5995   CheckExtension(extCV, isCV);
5996   CheckExtension(extXDMFHDF5, isXDMFHDF5);
5997 
5998 #undef CheckExtension
5999 
6000   if (isGmsh || isGmsh2 || isGmsh4) {
6001     PetscCall(DMPlexCreateGmshFromFile(comm, filename, interpolate, dm));
6002   } else if (isCGNS) {
6003     PetscCall(DMPlexCreateCGNSFromFile(comm, filename, interpolate, dm));
6004   } else if (isExodus || isGenesis) {
6005     PetscCall(DMPlexCreateExodusFromFile(comm, filename, interpolate, dm));
6006   } else if (isFluent) {
6007     PetscCall(DMPlexCreateFluentFromFile(comm, filename, interpolate, dm));
6008   } else if (isHDF5) {
6009     PetscViewer viewer;
6010 
6011     /* PETSC_VIEWER_HDF5_XDMF is used if the filename ends with .xdmf.h5, or if -dm_plex_create_from_hdf5_xdmf option is present */
6012     PetscCall(PetscOptionsGetBool(NULL, NULL, "-dm_plex_create_from_hdf5_xdmf", &isXDMFHDF5, NULL));
6013     PetscCall(PetscViewerCreate(comm, &viewer));
6014     PetscCall(PetscViewerSetType(viewer, PETSCVIEWERHDF5));
6015     PetscCall(PetscViewerSetOptionsPrefix(viewer, "dm_plex_create_"));
6016     PetscCall(PetscViewerSetFromOptions(viewer));
6017     PetscCall(PetscViewerFileSetMode(viewer, FILE_MODE_READ));
6018     PetscCall(PetscViewerFileSetName(viewer, filename));
6019 
6020     PetscCall(DMCreate(comm, dm));
6021     PetscCall(PetscObjectSetName((PetscObject)*dm, plexname));
6022     PetscCall(DMSetType(*dm, DMPLEX));
6023     if (isXDMFHDF5) PetscCall(PetscViewerPushFormat(viewer, PETSC_VIEWER_HDF5_XDMF));
6024     PetscCall(DMLoad(*dm, viewer));
6025     if (isXDMFHDF5) PetscCall(PetscViewerPopFormat(viewer));
6026     PetscCall(PetscViewerDestroy(&viewer));
6027 
6028     if (interpolate) {
6029       DM idm;
6030 
6031       PetscCall(DMPlexInterpolate(*dm, &idm));
6032       PetscCall(DMDestroy(dm));
6033       *dm = idm;
6034     }
6035   } else if (isPLY) {
6036     PetscCall(DMPlexCreatePLYFromFile(comm, filename, interpolate, dm));
6037   } else if (isEGADSLite || isEGADS || isIGES || isSTEP) {
6038     if (isEGADSLite) PetscCall(DMPlexCreateEGADSLiteFromFile(comm, filename, dm));
6039     else PetscCall(DMPlexCreateEGADSFromFile(comm, filename, dm));
6040     if (!interpolate) {
6041       DM udm;
6042 
6043       PetscCall(DMPlexUninterpolate(*dm, &udm));
6044       PetscCall(DMDestroy(dm));
6045       *dm = udm;
6046     }
6047   } else if (isCV) {
6048     PetscCall(DMPlexCreateCellVertexFromFile(comm, filename, interpolate, dm));
6049   } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cannot load file %s: unrecognized extension", filename);
6050   PetscCall(PetscStrlen(plexname, &len));
6051   if (len) PetscCall(PetscObjectSetName((PetscObject)*dm, plexname));
6052   PetscCall(PetscLogEventEnd(DMPLEX_CreateFromFile, 0, 0, 0, 0));
6053   PetscFunctionReturn(PETSC_SUCCESS);
6054 }
6055 
6056 /*@
6057   DMPlexCreateEphemeral - This takes a `DMPlexTransform` and a base `DMPlex` and produces an ephemeral `DM`, meaning one that is created on the fly in response to queries.
6058 
6059   Input Parameters:
6060 + tr     - The `DMPlexTransform`
6061 - prefix - An options prefix, or NULL
6062 
6063   Output Parameter:
6064 . dm - The `DM`
6065 
6066   Level: beginner
6067 
6068   Notes:
6069   An emphemeral mesh is one that is not stored concretely, as in the default `DMPLEX` implementation, but rather is produced on the fly in response to queries, using information from the transform and the base mesh.
6070 
6071 .seealso: `DMPlexCreateFromFile`, `DMPlexCreateFromDAG()`, `DMPlexCreateFromCellListPetsc()`, `DMPlexCreate()`
6072 @*/
6073 PetscErrorCode DMPlexCreateEphemeral(DMPlexTransform tr, const char prefix[], DM *dm)
6074 {
6075   DM           bdm, bcdm, cdm;
6076   Vec          coordinates, coordinatesNew;
6077   PetscSection cs;
6078   PetscInt     cdim, Nl;
6079 
6080   PetscFunctionBegin;
6081   PetscCall(DMCreate(PetscObjectComm((PetscObject)tr), dm));
6082   PetscCall(DMSetType(*dm, DMPLEX));
6083   ((DM_Plex *)(*dm)->data)->interpolated = DMPLEX_INTERPOLATED_FULL;
6084   // Handle coordinates
6085   PetscCall(DMPlexTransformGetDM(tr, &bdm));
6086   PetscCall(DMPlexTransformSetDimensions(tr, bdm, *dm));
6087   PetscCall(DMGetCoordinateDim(*dm, &cdim));
6088   PetscCall(DMGetCoordinateDM(bdm, &bcdm));
6089   PetscCall(DMGetCoordinateDM(*dm, &cdm));
6090   PetscCall(DMCopyDisc(bcdm, cdm));
6091   PetscCall(DMGetLocalSection(cdm, &cs));
6092   PetscCall(PetscSectionSetNumFields(cs, 1));
6093   PetscCall(PetscSectionSetFieldComponents(cs, 0, cdim));
6094   PetscCall(DMGetCoordinatesLocal(bdm, &coordinates));
6095   PetscCall(VecDuplicate(coordinates, &coordinatesNew));
6096   PetscCall(VecCopy(coordinates, coordinatesNew));
6097   PetscCall(DMSetCoordinatesLocal(*dm, coordinatesNew));
6098   PetscCall(VecDestroy(&coordinatesNew));
6099 
6100   PetscCall(PetscObjectReference((PetscObject)tr));
6101   PetscCall(DMPlexTransformDestroy(&((DM_Plex *)(*dm)->data)->tr));
6102   ((DM_Plex *)(*dm)->data)->tr = tr;
6103   PetscCall(DMPlexDistributeSetDefault(*dm, PETSC_FALSE));
6104   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*dm, prefix));
6105   PetscCall(DMSetFromOptions(*dm));
6106 
6107   PetscCall(DMGetNumLabels(bdm, &Nl));
6108   for (PetscInt l = 0; l < Nl; ++l) {
6109     DMLabel     label, labelNew;
6110     const char *lname;
6111     PetscBool   isDepth, isCellType;
6112 
6113     PetscCall(DMGetLabelName(bdm, l, &lname));
6114     PetscCall(PetscStrcmp(lname, "depth", &isDepth));
6115     if (isDepth) continue;
6116     PetscCall(PetscStrcmp(lname, "celltype", &isCellType));
6117     if (isCellType) continue;
6118     PetscCall(DMCreateLabel(*dm, lname));
6119     PetscCall(DMGetLabel(bdm, lname, &label));
6120     PetscCall(DMGetLabel(*dm, lname, &labelNew));
6121     PetscCall(DMLabelSetType(labelNew, DMLABELEPHEMERAL));
6122     PetscCall(DMLabelEphemeralSetLabel(labelNew, label));
6123     PetscCall(DMLabelEphemeralSetTransform(labelNew, tr));
6124     PetscCall(DMLabelSetUp(labelNew));
6125   }
6126   PetscFunctionReturn(PETSC_SUCCESS);
6127 }
6128