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