xref: /petsc/src/dm/impls/plex/plexcreate.c (revision 6725e60d8c32db1e3b1b8876c5777b87fd0aba60)
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) PetscCall(DMPlexCreateBoxMesh_Tensor_SFC_Internal(dm, dim, faces, lower, upper, periodicity, interpolate));
1295   else if (dim == 1) PetscCall(DMPlexCreateLineMesh_Internal(dm, faces[0], lower[0], upper[0], periodicity[0]));
1296   else if (simplex) PetscCall(DMPlexCreateBoxMesh_Simplex_Internal(dm, dim, faces, lower, upper, periodicity, interpolate));
1297   else PetscCall(DMPlexCreateBoxMesh_Tensor_Internal(dm, dim, faces, lower, upper, periodicity));
1298   if (!interpolate && dim > 1 && !simplex) {
1299     DM udm;
1300 
1301     PetscCall(DMPlexUninterpolate(dm, &udm));
1302     PetscCall(DMPlexCopyCoordinates(dm, udm));
1303     PetscCall(DMPlexReplace_Internal(dm, &udm));
1304   }
1305   PetscFunctionReturn(0);
1306 }
1307 
1308 /*@C
1309   DMPlexCreateBoxMesh - Creates a mesh on the tensor product of unit intervals (box) using simplices or tensor cells (hexahedra).
1310 
1311   Collective
1312 
1313   Input Parameters:
1314 + comm        - The communicator for the `DM` object
1315 . dim         - The spatial dimension
1316 . simplex     - `PETSC_TRUE` for simplices, `PETSC_FALSE` for tensor cells
1317 . faces       - Number of faces per dimension, or NULL for (1,) in 1D and (2, 2) in 2D and (1, 1, 1) in 3D
1318 . lower       - The lower left corner, or NULL for (0, 0, 0)
1319 . upper       - The upper right corner, or NULL for (1, 1, 1)
1320 . periodicity - The boundary type for the X,Y,Z direction, or NULL for `DM_BOUNDARY_NONE`
1321 - interpolate - Flag to create intermediate mesh pieces (edges, faces)
1322 
1323   Output Parameter:
1324 . dm  - The `DM` object
1325 
1326   Level: beginner
1327 
1328   Note:
1329    To customize this mesh using options, use
1330 .vb
1331   DMCreate(comm, &dm);
1332   DMSetType(dm, DMPLEX);
1333   DMSetFromOptions(dm);
1334 .ve
1335 and use the options in `DMSetFromOptions()`.
1336 
1337   Here is the numbering returned for 2 faces in each direction for tensor cells:
1338 .vb
1339  10---17---11---18----12
1340   |         |         |
1341   |         |         |
1342  20    2   22    3    24
1343   |         |         |
1344   |         |         |
1345   7---15----8---16----9
1346   |         |         |
1347   |         |         |
1348  19    0   21    1   23
1349   |         |         |
1350   |         |         |
1351   4---13----5---14----6
1352 .ve
1353 and for simplicial cells
1354 .vb
1355  14----8---15----9----16
1356   |\     5  |\      7 |
1357   | \       | \       |
1358  13   2    14    3    15
1359   | 4   \   | 6   \   |
1360   |       \ |       \ |
1361  11----6---12----7----13
1362   |\        |\        |
1363   | \    1  | \     3 |
1364  10   0    11    1    12
1365   | 0   \   | 2   \   |
1366   |       \ |       \ |
1367   8----4----9----5----10
1368 .ve
1369 
1370 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreateFromFile()`, `DMPlexCreateHexCylinderMesh()`, `DMSetType()`, `DMCreate()`
1371 @*/
1372 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)
1373 {
1374   PetscInt       fac[3] = {1, 1, 1};
1375   PetscReal      low[3] = {0, 0, 0};
1376   PetscReal      upp[3] = {1, 1, 1};
1377   DMBoundaryType bdt[3] = {DM_BOUNDARY_NONE, DM_BOUNDARY_NONE, DM_BOUNDARY_NONE};
1378 
1379   PetscFunctionBegin;
1380   PetscCall(DMCreate(comm, dm));
1381   PetscCall(DMSetType(*dm, DMPLEX));
1382   PetscCall(DMPlexCreateBoxMesh_Internal(*dm, dim, simplex, faces ? faces : fac, lower ? lower : low, upper ? upper : upp, periodicity ? periodicity : bdt, interpolate));
1383   if (periodicity) PetscCall(DMLocalizeCoordinates(*dm));
1384   PetscFunctionReturn(0);
1385 }
1386 
1387 static PetscErrorCode DMPlexCreateWedgeBoxMesh_Internal(DM dm, const PetscInt faces[], const PetscReal lower[], const PetscReal upper[], const DMBoundaryType periodicity[])
1388 {
1389   DM       bdm, vol;
1390   PetscInt i;
1391 
1392   PetscFunctionBegin;
1393   for (i = 0; i < 3; ++i) PetscCheck(periodicity[i] == DM_BOUNDARY_NONE, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Periodicity not yet supported");
1394   PetscCall(DMCreate(PetscObjectComm((PetscObject)dm), &bdm));
1395   PetscCall(DMSetType(bdm, DMPLEX));
1396   PetscCall(DMSetDimension(bdm, 2));
1397   PetscCall(DMPlexCreateBoxMesh_Simplex_Internal(bdm, 2, faces, lower, upper, periodicity, PETSC_TRUE));
1398   PetscCall(DMPlexExtrude(bdm, faces[2], upper[2] - lower[2], PETSC_TRUE, PETSC_FALSE, NULL, NULL, &vol));
1399   PetscCall(DMDestroy(&bdm));
1400   PetscCall(DMPlexReplace_Internal(dm, &vol));
1401   if (lower[2] != 0.0) {
1402     Vec          v;
1403     PetscScalar *x;
1404     PetscInt     cDim, n;
1405 
1406     PetscCall(DMGetCoordinatesLocal(dm, &v));
1407     PetscCall(VecGetBlockSize(v, &cDim));
1408     PetscCall(VecGetLocalSize(v, &n));
1409     PetscCall(VecGetArray(v, &x));
1410     x += cDim;
1411     for (i = 0; i < n; i += cDim) x[i] += lower[2];
1412     PetscCall(VecRestoreArray(v, &x));
1413     PetscCall(DMSetCoordinatesLocal(dm, v));
1414   }
1415   PetscFunctionReturn(0);
1416 }
1417 
1418 /*@
1419   DMPlexCreateWedgeBoxMesh - Creates a 3-D mesh tesselating the (x,y) plane and extruding in the third direction using wedge cells.
1420 
1421   Collective
1422 
1423   Input Parameters:
1424 + comm        - The communicator for the `DM` object
1425 . faces       - Number of faces per dimension, or NULL for (1, 1, 1)
1426 . lower       - The lower left corner, or NULL for (0, 0, 0)
1427 . upper       - The upper right corner, or NULL for (1, 1, 1)
1428 . periodicity - The boundary type for the X,Y,Z direction, or NULL for `DM_BOUNDARY_NONE`
1429 . orderHeight - If `PETSC_TRUE`, orders the extruded cells in the height first. Otherwise, orders the cell on the layers first
1430 - interpolate - Flag to create intermediate mesh pieces (edges, faces)
1431 
1432   Output Parameter:
1433 . dm  - The `DM` object
1434 
1435   Level: beginner
1436 
1437 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreateHexCylinderMesh()`, `DMPlexCreateWedgeCylinderMesh()`, `DMExtrude()`, `DMPlexCreateBoxMesh()`, `DMSetType()`, `DMCreate()`
1438 @*/
1439 PetscErrorCode DMPlexCreateWedgeBoxMesh(MPI_Comm comm, const PetscInt faces[], const PetscReal lower[], const PetscReal upper[], const DMBoundaryType periodicity[], PetscBool orderHeight, PetscBool interpolate, DM *dm)
1440 {
1441   PetscInt       fac[3] = {1, 1, 1};
1442   PetscReal      low[3] = {0, 0, 0};
1443   PetscReal      upp[3] = {1, 1, 1};
1444   DMBoundaryType bdt[3] = {DM_BOUNDARY_NONE, DM_BOUNDARY_NONE, DM_BOUNDARY_NONE};
1445 
1446   PetscFunctionBegin;
1447   PetscCall(DMCreate(comm, dm));
1448   PetscCall(DMSetType(*dm, DMPLEX));
1449   PetscCall(DMPlexCreateWedgeBoxMesh_Internal(*dm, faces ? faces : fac, lower ? lower : low, upper ? upper : upp, periodicity ? periodicity : bdt));
1450   if (!interpolate) {
1451     DM udm;
1452 
1453     PetscCall(DMPlexUninterpolate(*dm, &udm));
1454     PetscCall(DMPlexReplace_Internal(*dm, &udm));
1455   }
1456   if (periodicity) PetscCall(DMLocalizeCoordinates(*dm));
1457   PetscFunctionReturn(0);
1458 }
1459 
1460 /*
1461   DMPlexTensorPointLexicographic_Private - Returns all tuples of size 'len' with nonnegative integers that are all less than or equal to 'max' for that dimension.
1462                                            Ordering is lexicographic with lowest index as least significant in ordering.
1463                                            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}.
1464 
1465   Input Parameters:
1466 + len - The length of the tuple
1467 . max - The maximum for each dimension, so values are in [0, max)
1468 - tup - A tuple of length len+1: tup[len] > 0 indicates a stopping condition
1469 
1470   Output Parameter:
1471 . tup - A tuple of len integers whose entries are at most 'max'
1472 
1473   Level: developer
1474 
1475 .seealso: PetscDualSpaceTensorPointLexicographic_Internal(), PetscDualSpaceLatticePointLexicographic_Internal()
1476 */
1477 static PetscErrorCode DMPlexTensorPointLexicographic_Private(PetscInt len, const PetscInt max[], PetscInt tup[])
1478 {
1479   PetscInt i;
1480 
1481   PetscFunctionBegin;
1482   for (i = 0; i < len; ++i) {
1483     if (tup[i] < max[i] - 1) {
1484       break;
1485     } else {
1486       tup[i] = 0;
1487     }
1488   }
1489   if (i == len) tup[i - 1] = max[i - 1];
1490   else ++tup[i];
1491   PetscFunctionReturn(0);
1492 }
1493 
1494 static PetscInt TupleToIndex_Private(PetscInt len, const PetscInt max[], const PetscInt tup[])
1495 {
1496   PetscInt i, idx = tup[len - 1];
1497 
1498   for (i = len - 2; i >= 0; --i) {
1499     idx *= max[i];
1500     idx += tup[i];
1501   }
1502   return idx;
1503 }
1504 
1505 static PetscErrorCode DestroyExtent_Private(void *extent)
1506 {
1507   return PetscFree(extent);
1508 }
1509 
1510 static PetscErrorCode DMPlexCreateHypercubicMesh_Internal(DM dm, PetscInt dim, const PetscReal lower[], const PetscReal upper[], const PetscInt edges[], const DMBoundaryType bd[])
1511 {
1512   Vec          coordinates;
1513   PetscSection coordSection;
1514   DMLabel      cutLabel    = NULL;
1515   PetscBool    cutMarker   = PETSC_FALSE;
1516   PetscBool    periodic    = PETSC_FALSE;
1517   PetscInt     numCells    = 1, c;
1518   PetscInt     numVertices = 1, v;
1519   PetscScalar *coords;
1520   PetscInt    *vertices, *vert, *vtmp, *supp, cone[2];
1521   PetscInt     d, e, cell = 0, coordSize;
1522   PetscMPIInt  rank;
1523 
1524   PetscFunctionBegin;
1525   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1526   PetscCall(DMSetDimension(dm, dim));
1527   PetscCall(PetscCalloc4(dim, &vertices, dim, &vert, dim, &vtmp, 2 * dim, &supp));
1528   PetscCall(DMCreateLabel(dm, "marker"));
1529   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_periodic_cut", &cutMarker, NULL));
1530   for (d = 0; d < dim; ++d) periodic = (periodic || bd[d] == DM_BOUNDARY_PERIODIC) ? PETSC_TRUE : PETSC_FALSE;
1531   if (periodic && cutMarker) {
1532     PetscCall(DMCreateLabel(dm, "periodic_cut"));
1533     PetscCall(DMGetLabel(dm, "periodic_cut", &cutLabel));
1534   }
1535   for (d = 0; d < dim; ++d) PetscCheck(bd[d] == DM_BOUNDARY_PERIODIC, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Hypercubic mesh must be periodic now");
1536   for (d = 0; d < dim; ++d) {
1537     vertices[d] = edges[d];
1538     numVertices *= vertices[d];
1539   }
1540   numCells = numVertices * dim;
1541   PetscCall(DMPlexSetChart(dm, 0, numCells + numVertices));
1542   for (c = 0; c < numCells; ++c) PetscCall(DMPlexSetConeSize(dm, c, 2));
1543   for (v = numCells; v < numCells + numVertices; ++v) PetscCall(DMPlexSetSupportSize(dm, v, 2 * dim));
1544   /* TODO Loop over boundary and reset support sizes */
1545   PetscCall(DMSetUp(dm)); /* Allocate space for cones and supports */
1546   /* Build cell cones and vertex supports */
1547   PetscCall(DMCreateLabel(dm, "celltype"));
1548   while (vert[dim - 1] < vertices[dim - 1]) {
1549     const PetscInt vertex = TupleToIndex_Private(dim, vertices, vert) + numCells;
1550     PetscInt       s      = 0;
1551 
1552     PetscPrintf(PETSC_COMM_SELF, "Vertex %" PetscInt_FMT ":", vertex);
1553     for (d = 0; d < dim; ++d) PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, vert[d]);
1554     PetscPrintf(PETSC_COMM_SELF, "\n");
1555     PetscCall(DMPlexSetCellType(dm, vertex, DM_POLYTOPE_POINT));
1556     for (d = 0; d < dim; ++d) {
1557       for (e = 0; e < dim; ++e) vtmp[e] = vert[e];
1558       vtmp[d] = (vert[d] + 1) % vertices[d];
1559       cone[0] = vertex;
1560       cone[1] = TupleToIndex_Private(dim, vertices, vtmp) + numCells;
1561       PetscPrintf(PETSC_COMM_SELF, "  Vertex %" PetscInt_FMT ":", cone[1]);
1562       for (e = 0; e < dim; ++e) PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, vtmp[e]);
1563       PetscPrintf(PETSC_COMM_SELF, "\n");
1564       PetscCall(DMPlexSetCone(dm, cell, cone));
1565       PetscCall(DMPlexSetCellType(dm, cell, DM_POLYTOPE_SEGMENT));
1566       PetscPrintf(PETSC_COMM_SELF, "  Edge %" PetscInt_FMT " (%" PetscInt_FMT " %" PetscInt_FMT ")\n", cell, cone[0], cone[1]);
1567       ++cell;
1568     }
1569     for (d = 0; d < dim; ++d) {
1570       for (e = 0; e < dim; ++e) vtmp[e] = vert[e];
1571       vtmp[d]   = (vert[d] + vertices[d] - 1) % vertices[d];
1572       supp[s++] = TupleToIndex_Private(dim, vertices, vtmp) * dim + d;
1573       supp[s++] = (vertex - numCells) * dim + d;
1574       PetscCall(DMPlexSetSupport(dm, vertex, supp));
1575     }
1576     PetscCall(DMPlexTensorPointLexicographic_Private(dim, vertices, vert));
1577   }
1578   PetscCall(DMPlexStratify(dm));
1579   /* Build coordinates */
1580   PetscCall(DMGetCoordinateSection(dm, &coordSection));
1581   PetscCall(PetscSectionSetNumFields(coordSection, 1));
1582   PetscCall(PetscSectionSetFieldComponents(coordSection, 0, dim));
1583   PetscCall(PetscSectionSetChart(coordSection, numCells, numCells + numVertices));
1584   for (v = numCells; v < numCells + numVertices; ++v) {
1585     PetscCall(PetscSectionSetDof(coordSection, v, dim));
1586     PetscCall(PetscSectionSetFieldDof(coordSection, v, 0, dim));
1587   }
1588   PetscCall(PetscSectionSetUp(coordSection));
1589   PetscCall(PetscSectionGetStorageSize(coordSection, &coordSize));
1590   PetscCall(VecCreate(PETSC_COMM_SELF, &coordinates));
1591   PetscCall(PetscObjectSetName((PetscObject)coordinates, "coordinates"));
1592   PetscCall(VecSetSizes(coordinates, coordSize, PETSC_DETERMINE));
1593   PetscCall(VecSetBlockSize(coordinates, dim));
1594   PetscCall(VecSetType(coordinates, VECSTANDARD));
1595   PetscCall(VecGetArray(coordinates, &coords));
1596   for (d = 0; d < dim; ++d) vert[d] = 0;
1597   while (vert[dim - 1] < vertices[dim - 1]) {
1598     const PetscInt vertex = TupleToIndex_Private(dim, vertices, vert);
1599 
1600     for (d = 0; d < dim; ++d) coords[vertex * dim + d] = lower[d] + ((upper[d] - lower[d]) / vertices[d]) * vert[d];
1601     PetscPrintf(PETSC_COMM_SELF, "Vertex %" PetscInt_FMT ":", vertex);
1602     for (d = 0; d < dim; ++d) PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, vert[d]);
1603     for (d = 0; d < dim; ++d) PetscPrintf(PETSC_COMM_SELF, " %g", (double)PetscRealPart(coords[vertex * dim + d]));
1604     PetscPrintf(PETSC_COMM_SELF, "\n");
1605     PetscCall(DMPlexTensorPointLexicographic_Private(dim, vertices, vert));
1606   }
1607   PetscCall(VecRestoreArray(coordinates, &coords));
1608   PetscCall(DMSetCoordinatesLocal(dm, coordinates));
1609   PetscCall(VecDestroy(&coordinates));
1610   PetscCall(PetscFree4(vertices, vert, vtmp, supp));
1611   //PetscCall(DMSetPeriodicity(dm, NULL, lower, upper));
1612   // Attach the extent
1613   {
1614     PetscContainer c;
1615     PetscInt      *extent;
1616 
1617     PetscCall(PetscMalloc1(dim, &extent));
1618     for (PetscInt d = 0; d < dim; ++d) extent[d] = edges[d];
1619     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &c));
1620     PetscCall(PetscContainerSetUserDestroy(c, DestroyExtent_Private));
1621     PetscCall(PetscContainerSetPointer(c, extent));
1622     PetscCall(PetscObjectCompose((PetscObject)dm, "_extent", (PetscObject)c));
1623     PetscCall(PetscContainerDestroy(&c));
1624   }
1625   PetscFunctionReturn(0);
1626 }
1627 
1628 /*@C
1629   DMPlexCreateHypercubicMesh - Creates a peridoic mesh on the tensor product of unit intervals using only vertices and edges.
1630 
1631   Collective
1632 
1633   Input Parameters:
1634 + comm        - The communicator for the DM object
1635 . dim         - The spatial dimension
1636 . edges       - Number of edges per dimension, or NULL for (1,) in 1D and (2, 2) in 2D and (1, 1, 1) in 3D
1637 . lower       - The lower left corner, or NULL for (0, 0, 0)
1638 - upper       - The upper right corner, or NULL for (1, 1, 1)
1639 
1640   Output Parameter:
1641 . dm  - The DM object
1642 
1643   Note: If you want to customize this mesh using options, you just need to
1644 $  DMCreate(comm, &dm);
1645 $  DMSetType(dm, DMPLEX);
1646 $  DMSetFromOptions(dm);
1647 and use the options on the DMSetFromOptions() page.
1648 
1649   The vertices are numbered is lexicographic order, and the dim edges exiting a vertex in the positive orthant are number consecutively,
1650 $ 18--0-19--2-20--4-18
1651 $  |     |     |     |
1652 $ 13    15    17    13
1653 $  |     |     |     |
1654 $ 24-12-25-14-26-16-24
1655 $  |     |     |     |
1656 $  7     9    11     7
1657 $  |     |     |     |
1658 $ 21--6-22--8-23-10-21
1659 $  |     |     |     |
1660 $  1     3     5     1
1661 $  |     |     |     |
1662 $ 18--0-19--2-20--4-18
1663 
1664   Level: beginner
1665 
1666 .seealso: DMSetFromOptions(), DMPlexCreateFromFile(), DMPlexCreateHexCylinderMesh(), DMSetType(), DMCreate()
1667 @*/
1668 PetscErrorCode DMPlexCreateHypercubicMesh(MPI_Comm comm, PetscInt dim, const PetscInt edges[], const PetscReal lower[], const PetscReal upper[], DM *dm)
1669 {
1670   PetscInt       *edg;
1671   PetscReal      *low, *upp;
1672   DMBoundaryType *bdt;
1673   PetscInt        d;
1674 
1675   PetscFunctionBegin;
1676   PetscCall(DMCreate(comm, dm));
1677   PetscCall(DMSetType(*dm, DMPLEX));
1678   PetscCall(PetscMalloc4(dim, &edg, dim, &low, dim, &upp, dim, &bdt));
1679   for (d = 0; d < dim; ++d) {
1680     edg[d] = edges ? edges[d] : 1;
1681     low[d] = lower ? lower[d] : 0.;
1682     upp[d] = upper ? upper[d] : 1.;
1683     bdt[d] = DM_BOUNDARY_PERIODIC;
1684   }
1685   PetscCall(DMPlexCreateHypercubicMesh_Internal(*dm, dim, low, upp, edg, bdt));
1686   PetscCall(PetscFree4(edg, low, upp, bdt));
1687   PetscFunctionReturn(0);
1688 }
1689 
1690 /*@C
1691   DMPlexSetOptionsPrefix - Sets the prefix used for searching for all `DM` options in the database.
1692 
1693   Logically Collective on dm
1694 
1695   Input Parameters:
1696 + dm - the DM context
1697 - prefix - the prefix to prepend to all option names
1698 
1699   Level: advanced
1700 
1701   Note:
1702   A hyphen (-) must NOT be given at the beginning of the prefix name.
1703   The first character of all runtime options is AUTOMATICALLY the hyphen.
1704 
1705 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `SNESSetFromOptions()`
1706 @*/
1707 PetscErrorCode DMPlexSetOptionsPrefix(DM dm, const char prefix[])
1708 {
1709   DM_Plex *mesh = (DM_Plex *)dm->data;
1710 
1711   PetscFunctionBegin;
1712   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1713   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)dm, prefix));
1714   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)mesh->partitioner, prefix));
1715   PetscFunctionReturn(0);
1716 }
1717 
1718 /* Remap geometry to cylinder
1719    TODO: This only works for a single refinement, then it is broken
1720 
1721      Interior square: Linear interpolation is correct
1722      The other cells all have vertices on rays from the origin. We want to uniformly expand the spacing
1723      such that the last vertex is on the unit circle. So the closest and farthest vertices are at distance
1724 
1725        phi     = arctan(y/x)
1726        d_close = sqrt(1/8 + 1/4 sin^2(phi))
1727        d_far   = sqrt(1/2 + sin^2(phi))
1728 
1729      so we remap them using
1730 
1731        x_new = x_close + (x - x_close) (1 - d_close) / (d_far - d_close)
1732        y_new = y_close + (y - y_close) (1 - d_close) / (d_far - d_close)
1733 
1734      If pi/4 < phi < 3pi/4 or -3pi/4 < phi < -pi/4, then we switch x and y.
1735 */
1736 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[])
1737 {
1738   const PetscReal dis = 1.0 / PetscSqrtReal(2.0);
1739   const PetscReal ds2 = 0.5 * dis;
1740 
1741   if ((PetscAbsScalar(u[0]) <= ds2) && (PetscAbsScalar(u[1]) <= ds2)) {
1742     f0[0] = u[0];
1743     f0[1] = u[1];
1744   } else {
1745     PetscReal phi, sinp, cosp, dc, df, x, y, xc, yc;
1746 
1747     x    = PetscRealPart(u[0]);
1748     y    = PetscRealPart(u[1]);
1749     phi  = PetscAtan2Real(y, x);
1750     sinp = PetscSinReal(phi);
1751     cosp = PetscCosReal(phi);
1752     if ((PetscAbsReal(phi) > PETSC_PI / 4.0) && (PetscAbsReal(phi) < 3.0 * PETSC_PI / 4.0)) {
1753       dc = PetscAbsReal(ds2 / sinp);
1754       df = PetscAbsReal(dis / sinp);
1755       xc = ds2 * x / PetscAbsReal(y);
1756       yc = ds2 * PetscSignReal(y);
1757     } else {
1758       dc = PetscAbsReal(ds2 / cosp);
1759       df = PetscAbsReal(dis / cosp);
1760       xc = ds2 * PetscSignReal(x);
1761       yc = ds2 * y / PetscAbsReal(x);
1762     }
1763     f0[0] = xc + (u[0] - xc) * (1.0 - dc) / (df - dc);
1764     f0[1] = yc + (u[1] - yc) * (1.0 - dc) / (df - dc);
1765   }
1766   f0[2] = u[2];
1767 }
1768 
1769 static PetscErrorCode DMPlexCreateHexCylinderMesh_Internal(DM dm, DMBoundaryType periodicZ)
1770 {
1771   const PetscInt dim = 3;
1772   PetscInt       numCells, numVertices;
1773   PetscMPIInt    rank;
1774 
1775   PetscFunctionBegin;
1776   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1777   PetscCall(DMSetDimension(dm, dim));
1778   /* Create topology */
1779   {
1780     PetscInt cone[8], c;
1781 
1782     numCells    = rank == 0 ? 5 : 0;
1783     numVertices = rank == 0 ? 16 : 0;
1784     if (periodicZ == DM_BOUNDARY_PERIODIC) {
1785       numCells *= 3;
1786       numVertices = rank == 0 ? 24 : 0;
1787     }
1788     PetscCall(DMPlexSetChart(dm, 0, numCells + numVertices));
1789     for (c = 0; c < numCells; c++) PetscCall(DMPlexSetConeSize(dm, c, 8));
1790     PetscCall(DMSetUp(dm));
1791     if (rank == 0) {
1792       if (periodicZ == DM_BOUNDARY_PERIODIC) {
1793         cone[0] = 15;
1794         cone[1] = 18;
1795         cone[2] = 17;
1796         cone[3] = 16;
1797         cone[4] = 31;
1798         cone[5] = 32;
1799         cone[6] = 33;
1800         cone[7] = 34;
1801         PetscCall(DMPlexSetCone(dm, 0, cone));
1802         cone[0] = 16;
1803         cone[1] = 17;
1804         cone[2] = 24;
1805         cone[3] = 23;
1806         cone[4] = 32;
1807         cone[5] = 36;
1808         cone[6] = 37;
1809         cone[7] = 33; /* 22 25 26 21 */
1810         PetscCall(DMPlexSetCone(dm, 1, cone));
1811         cone[0] = 18;
1812         cone[1] = 27;
1813         cone[2] = 24;
1814         cone[3] = 17;
1815         cone[4] = 34;
1816         cone[5] = 33;
1817         cone[6] = 37;
1818         cone[7] = 38;
1819         PetscCall(DMPlexSetCone(dm, 2, cone));
1820         cone[0] = 29;
1821         cone[1] = 27;
1822         cone[2] = 18;
1823         cone[3] = 15;
1824         cone[4] = 35;
1825         cone[5] = 31;
1826         cone[6] = 34;
1827         cone[7] = 38;
1828         PetscCall(DMPlexSetCone(dm, 3, cone));
1829         cone[0] = 29;
1830         cone[1] = 15;
1831         cone[2] = 16;
1832         cone[3] = 23;
1833         cone[4] = 35;
1834         cone[5] = 36;
1835         cone[6] = 32;
1836         cone[7] = 31;
1837         PetscCall(DMPlexSetCone(dm, 4, cone));
1838 
1839         cone[0] = 31;
1840         cone[1] = 34;
1841         cone[2] = 33;
1842         cone[3] = 32;
1843         cone[4] = 19;
1844         cone[5] = 22;
1845         cone[6] = 21;
1846         cone[7] = 20;
1847         PetscCall(DMPlexSetCone(dm, 5, cone));
1848         cone[0] = 32;
1849         cone[1] = 33;
1850         cone[2] = 37;
1851         cone[3] = 36;
1852         cone[4] = 22;
1853         cone[5] = 25;
1854         cone[6] = 26;
1855         cone[7] = 21;
1856         PetscCall(DMPlexSetCone(dm, 6, cone));
1857         cone[0] = 34;
1858         cone[1] = 38;
1859         cone[2] = 37;
1860         cone[3] = 33;
1861         cone[4] = 20;
1862         cone[5] = 21;
1863         cone[6] = 26;
1864         cone[7] = 28;
1865         PetscCall(DMPlexSetCone(dm, 7, cone));
1866         cone[0] = 35;
1867         cone[1] = 38;
1868         cone[2] = 34;
1869         cone[3] = 31;
1870         cone[4] = 30;
1871         cone[5] = 19;
1872         cone[6] = 20;
1873         cone[7] = 28;
1874         PetscCall(DMPlexSetCone(dm, 8, cone));
1875         cone[0] = 35;
1876         cone[1] = 31;
1877         cone[2] = 32;
1878         cone[3] = 36;
1879         cone[4] = 30;
1880         cone[5] = 25;
1881         cone[6] = 22;
1882         cone[7] = 19;
1883         PetscCall(DMPlexSetCone(dm, 9, cone));
1884 
1885         cone[0] = 19;
1886         cone[1] = 20;
1887         cone[2] = 21;
1888         cone[3] = 22;
1889         cone[4] = 15;
1890         cone[5] = 16;
1891         cone[6] = 17;
1892         cone[7] = 18;
1893         PetscCall(DMPlexSetCone(dm, 10, cone));
1894         cone[0] = 22;
1895         cone[1] = 21;
1896         cone[2] = 26;
1897         cone[3] = 25;
1898         cone[4] = 16;
1899         cone[5] = 23;
1900         cone[6] = 24;
1901         cone[7] = 17;
1902         PetscCall(DMPlexSetCone(dm, 11, cone));
1903         cone[0] = 20;
1904         cone[1] = 28;
1905         cone[2] = 26;
1906         cone[3] = 21;
1907         cone[4] = 18;
1908         cone[5] = 17;
1909         cone[6] = 24;
1910         cone[7] = 27;
1911         PetscCall(DMPlexSetCone(dm, 12, cone));
1912         cone[0] = 30;
1913         cone[1] = 28;
1914         cone[2] = 20;
1915         cone[3] = 19;
1916         cone[4] = 29;
1917         cone[5] = 15;
1918         cone[6] = 18;
1919         cone[7] = 27;
1920         PetscCall(DMPlexSetCone(dm, 13, cone));
1921         cone[0] = 30;
1922         cone[1] = 19;
1923         cone[2] = 22;
1924         cone[3] = 25;
1925         cone[4] = 29;
1926         cone[5] = 23;
1927         cone[6] = 16;
1928         cone[7] = 15;
1929         PetscCall(DMPlexSetCone(dm, 14, cone));
1930       } else {
1931         cone[0] = 5;
1932         cone[1] = 8;
1933         cone[2] = 7;
1934         cone[3] = 6;
1935         cone[4] = 9;
1936         cone[5] = 12;
1937         cone[6] = 11;
1938         cone[7] = 10;
1939         PetscCall(DMPlexSetCone(dm, 0, cone));
1940         cone[0] = 6;
1941         cone[1] = 7;
1942         cone[2] = 14;
1943         cone[3] = 13;
1944         cone[4] = 12;
1945         cone[5] = 15;
1946         cone[6] = 16;
1947         cone[7] = 11;
1948         PetscCall(DMPlexSetCone(dm, 1, cone));
1949         cone[0] = 8;
1950         cone[1] = 17;
1951         cone[2] = 14;
1952         cone[3] = 7;
1953         cone[4] = 10;
1954         cone[5] = 11;
1955         cone[6] = 16;
1956         cone[7] = 18;
1957         PetscCall(DMPlexSetCone(dm, 2, cone));
1958         cone[0] = 19;
1959         cone[1] = 17;
1960         cone[2] = 8;
1961         cone[3] = 5;
1962         cone[4] = 20;
1963         cone[5] = 9;
1964         cone[6] = 10;
1965         cone[7] = 18;
1966         PetscCall(DMPlexSetCone(dm, 3, cone));
1967         cone[0] = 19;
1968         cone[1] = 5;
1969         cone[2] = 6;
1970         cone[3] = 13;
1971         cone[4] = 20;
1972         cone[5] = 15;
1973         cone[6] = 12;
1974         cone[7] = 9;
1975         PetscCall(DMPlexSetCone(dm, 4, cone));
1976       }
1977     }
1978     PetscCall(DMPlexSymmetrize(dm));
1979     PetscCall(DMPlexStratify(dm));
1980   }
1981   /* Create cube geometry */
1982   {
1983     Vec             coordinates;
1984     PetscSection    coordSection;
1985     PetscScalar    *coords;
1986     PetscInt        coordSize, v;
1987     const PetscReal dis = 1.0 / PetscSqrtReal(2.0);
1988     const PetscReal ds2 = dis / 2.0;
1989 
1990     /* Build coordinates */
1991     PetscCall(DMGetCoordinateSection(dm, &coordSection));
1992     PetscCall(PetscSectionSetNumFields(coordSection, 1));
1993     PetscCall(PetscSectionSetFieldComponents(coordSection, 0, dim));
1994     PetscCall(PetscSectionSetChart(coordSection, numCells, numCells + numVertices));
1995     for (v = numCells; v < numCells + numVertices; ++v) {
1996       PetscCall(PetscSectionSetDof(coordSection, v, dim));
1997       PetscCall(PetscSectionSetFieldDof(coordSection, v, 0, dim));
1998     }
1999     PetscCall(PetscSectionSetUp(coordSection));
2000     PetscCall(PetscSectionGetStorageSize(coordSection, &coordSize));
2001     PetscCall(VecCreate(PETSC_COMM_SELF, &coordinates));
2002     PetscCall(PetscObjectSetName((PetscObject)coordinates, "coordinates"));
2003     PetscCall(VecSetSizes(coordinates, coordSize, PETSC_DETERMINE));
2004     PetscCall(VecSetBlockSize(coordinates, dim));
2005     PetscCall(VecSetType(coordinates, VECSTANDARD));
2006     PetscCall(VecGetArray(coordinates, &coords));
2007     if (rank == 0) {
2008       coords[0 * dim + 0]  = -ds2;
2009       coords[0 * dim + 1]  = -ds2;
2010       coords[0 * dim + 2]  = 0.0;
2011       coords[1 * dim + 0]  = ds2;
2012       coords[1 * dim + 1]  = -ds2;
2013       coords[1 * dim + 2]  = 0.0;
2014       coords[2 * dim + 0]  = ds2;
2015       coords[2 * dim + 1]  = ds2;
2016       coords[2 * dim + 2]  = 0.0;
2017       coords[3 * dim + 0]  = -ds2;
2018       coords[3 * dim + 1]  = ds2;
2019       coords[3 * dim + 2]  = 0.0;
2020       coords[4 * dim + 0]  = -ds2;
2021       coords[4 * dim + 1]  = -ds2;
2022       coords[4 * dim + 2]  = 1.0;
2023       coords[5 * dim + 0]  = -ds2;
2024       coords[5 * dim + 1]  = ds2;
2025       coords[5 * dim + 2]  = 1.0;
2026       coords[6 * dim + 0]  = ds2;
2027       coords[6 * dim + 1]  = ds2;
2028       coords[6 * dim + 2]  = 1.0;
2029       coords[7 * dim + 0]  = ds2;
2030       coords[7 * dim + 1]  = -ds2;
2031       coords[7 * dim + 2]  = 1.0;
2032       coords[8 * dim + 0]  = dis;
2033       coords[8 * dim + 1]  = -dis;
2034       coords[8 * dim + 2]  = 0.0;
2035       coords[9 * dim + 0]  = dis;
2036       coords[9 * dim + 1]  = dis;
2037       coords[9 * dim + 2]  = 0.0;
2038       coords[10 * dim + 0] = dis;
2039       coords[10 * dim + 1] = -dis;
2040       coords[10 * dim + 2] = 1.0;
2041       coords[11 * dim + 0] = dis;
2042       coords[11 * dim + 1] = dis;
2043       coords[11 * dim + 2] = 1.0;
2044       coords[12 * dim + 0] = -dis;
2045       coords[12 * dim + 1] = dis;
2046       coords[12 * dim + 2] = 0.0;
2047       coords[13 * dim + 0] = -dis;
2048       coords[13 * dim + 1] = dis;
2049       coords[13 * dim + 2] = 1.0;
2050       coords[14 * dim + 0] = -dis;
2051       coords[14 * dim + 1] = -dis;
2052       coords[14 * dim + 2] = 0.0;
2053       coords[15 * dim + 0] = -dis;
2054       coords[15 * dim + 1] = -dis;
2055       coords[15 * dim + 2] = 1.0;
2056       if (periodicZ == DM_BOUNDARY_PERIODIC) {
2057         /* 15 31 19 */ coords[16 * dim + 0] = -ds2;
2058         coords[16 * dim + 1]                = -ds2;
2059         coords[16 * dim + 2]                = 0.5;
2060         /* 16 32 22 */ coords[17 * dim + 0] = ds2;
2061         coords[17 * dim + 1]                = -ds2;
2062         coords[17 * dim + 2]                = 0.5;
2063         /* 17 33 21 */ coords[18 * dim + 0] = ds2;
2064         coords[18 * dim + 1]                = ds2;
2065         coords[18 * dim + 2]                = 0.5;
2066         /* 18 34 20 */ coords[19 * dim + 0] = -ds2;
2067         coords[19 * dim + 1]                = ds2;
2068         coords[19 * dim + 2]                = 0.5;
2069         /* 29 35 30 */ coords[20 * dim + 0] = -dis;
2070         coords[20 * dim + 1]                = -dis;
2071         coords[20 * dim + 2]                = 0.5;
2072         /* 23 36 25 */ coords[21 * dim + 0] = dis;
2073         coords[21 * dim + 1]                = -dis;
2074         coords[21 * dim + 2]                = 0.5;
2075         /* 24 37 26 */ coords[22 * dim + 0] = dis;
2076         coords[22 * dim + 1]                = dis;
2077         coords[22 * dim + 2]                = 0.5;
2078         /* 27 38 28 */ coords[23 * dim + 0] = -dis;
2079         coords[23 * dim + 1]                = dis;
2080         coords[23 * dim + 2]                = 0.5;
2081       }
2082     }
2083     PetscCall(VecRestoreArray(coordinates, &coords));
2084     PetscCall(DMSetCoordinatesLocal(dm, coordinates));
2085     PetscCall(VecDestroy(&coordinates));
2086   }
2087   /* Create periodicity */
2088   if (periodicZ == DM_BOUNDARY_PERIODIC || periodicZ == DM_BOUNDARY_TWIST) {
2089     PetscReal L[3]       = {-1., -1., 0.};
2090     PetscReal maxCell[3] = {-1., -1., 0.};
2091     PetscReal lower[3]   = {0.0, 0.0, 0.0};
2092     PetscReal upper[3]   = {1.0, 1.0, 1.5};
2093     PetscInt  numZCells  = 3;
2094 
2095     L[2]       = upper[2] - lower[2];
2096     maxCell[2] = 1.1 * (L[2] / numZCells);
2097     PetscCall(DMSetPeriodicity(dm, maxCell, lower, L));
2098   }
2099   {
2100     DM          cdm;
2101     PetscDS     cds;
2102     PetscScalar c[2] = {1.0, 1.0};
2103 
2104     PetscCall(DMPlexCreateCoordinateSpace(dm, 1, snapToCylinder));
2105     PetscCall(DMGetCoordinateDM(dm, &cdm));
2106     PetscCall(DMGetDS(cdm, &cds));
2107     PetscCall(PetscDSSetConstants(cds, 2, c));
2108   }
2109   /* Wait for coordinate creation before doing in-place modification */
2110   PetscCall(DMPlexInterpolateInPlace_Internal(dm));
2111   PetscFunctionReturn(0);
2112 }
2113 
2114 /*@
2115   DMPlexCreateHexCylinderMesh - Creates a mesh on the tensor product of the unit interval with the circle (cylinder) using hexahedra.
2116 
2117   Collective
2118 
2119   Input Parameters:
2120 + comm      - The communicator for the `DM` object
2121 - periodicZ - The boundary type for the Z direction
2122 
2123   Output Parameter:
2124 . dm  - The DM object
2125 
2126   Level: beginner
2127 
2128   Note:
2129   Here is the output numbering looking from the bottom of the cylinder:
2130 .vb
2131        17-----14
2132         |     |
2133         |  2  |
2134         |     |
2135  17-----8-----7-----14
2136   |     |     |     |
2137   |  3  |  0  |  1  |
2138   |     |     |     |
2139  19-----5-----6-----13
2140         |     |
2141         |  4  |
2142         |     |
2143        19-----13
2144 
2145  and up through the top
2146 
2147        18-----16
2148         |     |
2149         |  2  |
2150         |     |
2151  18----10----11-----16
2152   |     |     |     |
2153   |  3  |  0  |  1  |
2154   |     |     |     |
2155  20-----9----12-----15
2156         |     |
2157         |  4  |
2158         |     |
2159        20-----15
2160 .ve
2161 
2162 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreateBoxMesh()`, `DMSetType()`, `DMCreate()`
2163 @*/
2164 PetscErrorCode DMPlexCreateHexCylinderMesh(MPI_Comm comm, DMBoundaryType periodicZ, DM *dm)
2165 {
2166   PetscFunctionBegin;
2167   PetscValidPointer(dm, 3);
2168   PetscCall(DMCreate(comm, dm));
2169   PetscCall(DMSetType(*dm, DMPLEX));
2170   PetscCall(DMPlexCreateHexCylinderMesh_Internal(*dm, periodicZ));
2171   PetscFunctionReturn(0);
2172 }
2173 
2174 static PetscErrorCode DMPlexCreateWedgeCylinderMesh_Internal(DM dm, PetscInt n, PetscBool interpolate)
2175 {
2176   const PetscInt dim = 3;
2177   PetscInt       numCells, numVertices, v;
2178   PetscMPIInt    rank;
2179 
2180   PetscFunctionBegin;
2181   PetscCheck(n >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of wedges %" PetscInt_FMT " cannot be negative", n);
2182   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
2183   PetscCall(DMSetDimension(dm, dim));
2184   /* Must create the celltype label here so that we do not automatically try to compute the types */
2185   PetscCall(DMCreateLabel(dm, "celltype"));
2186   /* Create topology */
2187   {
2188     PetscInt cone[6], c;
2189 
2190     numCells    = rank == 0 ? n : 0;
2191     numVertices = rank == 0 ? 2 * (n + 1) : 0;
2192     PetscCall(DMPlexSetChart(dm, 0, numCells + numVertices));
2193     for (c = 0; c < numCells; c++) PetscCall(DMPlexSetConeSize(dm, c, 6));
2194     PetscCall(DMSetUp(dm));
2195     for (c = 0; c < numCells; c++) {
2196       cone[0] = c + n * 1;
2197       cone[1] = (c + 1) % n + n * 1;
2198       cone[2] = 0 + 3 * n;
2199       cone[3] = c + n * 2;
2200       cone[4] = (c + 1) % n + n * 2;
2201       cone[5] = 1 + 3 * n;
2202       PetscCall(DMPlexSetCone(dm, c, cone));
2203       PetscCall(DMPlexSetCellType(dm, c, DM_POLYTOPE_TRI_PRISM_TENSOR));
2204     }
2205     PetscCall(DMPlexSymmetrize(dm));
2206     PetscCall(DMPlexStratify(dm));
2207   }
2208   for (v = numCells; v < numCells + numVertices; ++v) PetscCall(DMPlexSetCellType(dm, v, DM_POLYTOPE_POINT));
2209   /* Create cylinder geometry */
2210   {
2211     Vec          coordinates;
2212     PetscSection coordSection;
2213     PetscScalar *coords;
2214     PetscInt     coordSize, c;
2215 
2216     /* Build coordinates */
2217     PetscCall(DMGetCoordinateSection(dm, &coordSection));
2218     PetscCall(PetscSectionSetNumFields(coordSection, 1));
2219     PetscCall(PetscSectionSetFieldComponents(coordSection, 0, dim));
2220     PetscCall(PetscSectionSetChart(coordSection, numCells, numCells + numVertices));
2221     for (v = numCells; v < numCells + numVertices; ++v) {
2222       PetscCall(PetscSectionSetDof(coordSection, v, dim));
2223       PetscCall(PetscSectionSetFieldDof(coordSection, v, 0, dim));
2224     }
2225     PetscCall(PetscSectionSetUp(coordSection));
2226     PetscCall(PetscSectionGetStorageSize(coordSection, &coordSize));
2227     PetscCall(VecCreate(PETSC_COMM_SELF, &coordinates));
2228     PetscCall(PetscObjectSetName((PetscObject)coordinates, "coordinates"));
2229     PetscCall(VecSetSizes(coordinates, coordSize, PETSC_DETERMINE));
2230     PetscCall(VecSetBlockSize(coordinates, dim));
2231     PetscCall(VecSetType(coordinates, VECSTANDARD));
2232     PetscCall(VecGetArray(coordinates, &coords));
2233     for (c = 0; c < numCells; c++) {
2234       coords[(c + 0 * n) * dim + 0] = PetscCosReal(2.0 * c * PETSC_PI / n);
2235       coords[(c + 0 * n) * dim + 1] = PetscSinReal(2.0 * c * PETSC_PI / n);
2236       coords[(c + 0 * n) * dim + 2] = 1.0;
2237       coords[(c + 1 * n) * dim + 0] = PetscCosReal(2.0 * c * PETSC_PI / n);
2238       coords[(c + 1 * n) * dim + 1] = PetscSinReal(2.0 * c * PETSC_PI / n);
2239       coords[(c + 1 * n) * dim + 2] = 0.0;
2240     }
2241     if (rank == 0) {
2242       coords[(2 * n + 0) * dim + 0] = 0.0;
2243       coords[(2 * n + 0) * dim + 1] = 0.0;
2244       coords[(2 * n + 0) * dim + 2] = 1.0;
2245       coords[(2 * n + 1) * dim + 0] = 0.0;
2246       coords[(2 * n + 1) * dim + 1] = 0.0;
2247       coords[(2 * n + 1) * dim + 2] = 0.0;
2248     }
2249     PetscCall(VecRestoreArray(coordinates, &coords));
2250     PetscCall(DMSetCoordinatesLocal(dm, coordinates));
2251     PetscCall(VecDestroy(&coordinates));
2252   }
2253   /* Interpolate */
2254   if (interpolate) PetscCall(DMPlexInterpolateInPlace_Internal(dm));
2255   PetscFunctionReturn(0);
2256 }
2257 
2258 /*@
2259   DMPlexCreateWedgeCylinderMesh - Creates a mesh on the tensor product of the unit interval with the circle (cylinder) using wedges.
2260 
2261   Collective
2262 
2263   Input Parameters:
2264 + comm - The communicator for the `DM` object
2265 . n    - The number of wedges around the origin
2266 - interpolate - Create edges and faces
2267 
2268   Output Parameter:
2269 . dm  - The `DM` object
2270 
2271   Level: beginner
2272 
2273 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreateHexCylinderMesh()`, `DMPlexCreateBoxMesh()`, `DMSetType()`, `DMCreate()`
2274 @*/
2275 PetscErrorCode DMPlexCreateWedgeCylinderMesh(MPI_Comm comm, PetscInt n, PetscBool interpolate, DM *dm)
2276 {
2277   PetscFunctionBegin;
2278   PetscValidPointer(dm, 4);
2279   PetscCall(DMCreate(comm, dm));
2280   PetscCall(DMSetType(*dm, DMPLEX));
2281   PetscCall(DMPlexCreateWedgeCylinderMesh_Internal(*dm, n, interpolate));
2282   PetscFunctionReturn(0);
2283 }
2284 
2285 static inline PetscReal DiffNormReal(PetscInt dim, const PetscReal x[], const PetscReal y[])
2286 {
2287   PetscReal prod = 0.0;
2288   PetscInt  i;
2289   for (i = 0; i < dim; ++i) prod += PetscSqr(x[i] - y[i]);
2290   return PetscSqrtReal(prod);
2291 }
2292 static inline PetscReal DotReal(PetscInt dim, const PetscReal x[], const PetscReal y[])
2293 {
2294   PetscReal prod = 0.0;
2295   PetscInt  i;
2296   for (i = 0; i < dim; ++i) prod += x[i] * y[i];
2297   return prod;
2298 }
2299 
2300 /* The first constant is the sphere radius */
2301 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[])
2302 {
2303   PetscReal r     = PetscRealPart(constants[0]);
2304   PetscReal norm2 = 0.0, fac;
2305   PetscInt  n     = uOff[1] - uOff[0], d;
2306 
2307   for (d = 0; d < n; ++d) norm2 += PetscSqr(PetscRealPart(u[d]));
2308   fac = r / PetscSqrtReal(norm2);
2309   for (d = 0; d < n; ++d) f0[d] = u[d] * fac;
2310 }
2311 
2312 static PetscErrorCode DMPlexCreateSphereMesh_Internal(DM dm, PetscInt dim, PetscBool simplex, PetscReal R)
2313 {
2314   const PetscInt embedDim = dim + 1;
2315   PetscSection   coordSection;
2316   Vec            coordinates;
2317   PetscScalar   *coords;
2318   PetscReal     *coordsIn;
2319   PetscInt       numCells, numEdges, numVerts = 0, firstVertex = 0, v, firstEdge, coordSize, d, c, e;
2320   PetscMPIInt    rank;
2321 
2322   PetscFunctionBegin;
2323   PetscValidLogicalCollectiveBool(dm, simplex, 3);
2324   PetscCall(DMSetDimension(dm, dim));
2325   PetscCall(DMSetCoordinateDim(dm, dim + 1));
2326   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
2327   switch (dim) {
2328   case 2:
2329     if (simplex) {
2330       const PetscReal radius    = PetscSqrtReal(1 + PETSC_PHI * PETSC_PHI) / (1.0 + PETSC_PHI);
2331       const PetscReal edgeLen   = 2.0 / (1.0 + PETSC_PHI) * (R / radius);
2332       const PetscInt  degree    = 5;
2333       PetscReal       vertex[3] = {0.0, 1.0 / (1.0 + PETSC_PHI), PETSC_PHI / (1.0 + PETSC_PHI)};
2334       PetscInt        s[3]      = {1, 1, 1};
2335       PetscInt        cone[3];
2336       PetscInt       *graph, p, i, j, k;
2337 
2338       vertex[0] *= R / radius;
2339       vertex[1] *= R / radius;
2340       vertex[2] *= R / radius;
2341       numCells    = rank == 0 ? 20 : 0;
2342       numVerts    = rank == 0 ? 12 : 0;
2343       firstVertex = numCells;
2344       /* Use icosahedron, which for a R-sphere has coordinates which are all cyclic permutations of
2345 
2346            (0, \pm 1/\phi+1, \pm \phi/\phi+1)
2347 
2348          where \phi^2 - \phi - 1 = 0, meaning \phi is the golden ratio \frac{1 + \sqrt{5}}{2}. The edge
2349          length is then given by 2/(1+\phi) = 2 * 0.38197 = 0.76393.
2350       */
2351       /* Construct vertices */
2352       PetscCall(PetscCalloc1(numVerts * embedDim, &coordsIn));
2353       if (rank == 0) {
2354         for (p = 0, i = 0; p < embedDim; ++p) {
2355           for (s[1] = -1; s[1] < 2; s[1] += 2) {
2356             for (s[2] = -1; s[2] < 2; s[2] += 2) {
2357               for (d = 0; d < embedDim; ++d) coordsIn[i * embedDim + d] = s[(d + p) % embedDim] * vertex[(d + p) % embedDim];
2358               ++i;
2359             }
2360           }
2361         }
2362       }
2363       /* Construct graph */
2364       PetscCall(PetscCalloc1(numVerts * numVerts, &graph));
2365       for (i = 0; i < numVerts; ++i) {
2366         for (j = 0, k = 0; j < numVerts; ++j) {
2367           if (PetscAbsReal(DiffNormReal(embedDim, &coordsIn[i * embedDim], &coordsIn[j * embedDim]) - edgeLen) < PETSC_SMALL) {
2368             graph[i * numVerts + j] = 1;
2369             ++k;
2370           }
2371         }
2372         PetscCheck(k == degree, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid icosahedron, vertex %" PetscInt_FMT " degree %" PetscInt_FMT " != %" PetscInt_FMT, i, k, degree);
2373       }
2374       /* Build Topology */
2375       PetscCall(DMPlexSetChart(dm, 0, numCells + numVerts));
2376       for (c = 0; c < numCells; c++) PetscCall(DMPlexSetConeSize(dm, c, embedDim));
2377       PetscCall(DMSetUp(dm)); /* Allocate space for cones */
2378       /* Cells */
2379       for (i = 0, c = 0; i < numVerts; ++i) {
2380         for (j = 0; j < i; ++j) {
2381           for (k = 0; k < j; ++k) {
2382             if (graph[i * numVerts + j] && graph[j * numVerts + k] && graph[k * numVerts + i]) {
2383               cone[0] = firstVertex + i;
2384               cone[1] = firstVertex + j;
2385               cone[2] = firstVertex + k;
2386               /* Check orientation */
2387               {
2388                 const PetscInt epsilon[3][3][3] = {
2389                   {{0, 0, 0},  {0, 0, 1},  {0, -1, 0}},
2390                   {{0, 0, -1}, {0, 0, 0},  {1, 0, 0} },
2391                   {{0, 1, 0},  {-1, 0, 0}, {0, 0, 0} }
2392                 };
2393                 PetscReal normal[3];
2394                 PetscInt  e, f;
2395 
2396                 for (d = 0; d < embedDim; ++d) {
2397                   normal[d] = 0.0;
2398                   for (e = 0; e < embedDim; ++e) {
2399                     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]);
2400                   }
2401                 }
2402                 if (DotReal(embedDim, normal, &coordsIn[i * embedDim]) < 0) {
2403                   PetscInt tmp = cone[1];
2404                   cone[1]      = cone[2];
2405                   cone[2]      = tmp;
2406                 }
2407               }
2408               PetscCall(DMPlexSetCone(dm, c++, cone));
2409             }
2410           }
2411         }
2412       }
2413       PetscCall(DMPlexSymmetrize(dm));
2414       PetscCall(DMPlexStratify(dm));
2415       PetscCall(PetscFree(graph));
2416     } else {
2417       /*
2418         12-21--13
2419          |     |
2420         25  4  24
2421          |     |
2422   12-25--9-16--8-24--13
2423    |     |     |     |
2424   23  5 17  0 15  3  22
2425    |     |     |     |
2426   10-20--6-14--7-19--11
2427          |     |
2428         20  1  19
2429          |     |
2430         10-18--11
2431          |     |
2432         23  2  22
2433          |     |
2434         12-21--13
2435        */
2436       PetscInt cone[4], ornt[4];
2437 
2438       numCells    = rank == 0 ? 6 : 0;
2439       numEdges    = rank == 0 ? 12 : 0;
2440       numVerts    = rank == 0 ? 8 : 0;
2441       firstVertex = numCells;
2442       firstEdge   = numCells + numVerts;
2443       /* Build Topology */
2444       PetscCall(DMPlexSetChart(dm, 0, numCells + numEdges + numVerts));
2445       for (c = 0; c < numCells; c++) PetscCall(DMPlexSetConeSize(dm, c, 4));
2446       for (e = firstEdge; e < firstEdge + numEdges; ++e) PetscCall(DMPlexSetConeSize(dm, e, 2));
2447       PetscCall(DMSetUp(dm)); /* Allocate space for cones */
2448       if (rank == 0) {
2449         /* Cell 0 */
2450         cone[0] = 14;
2451         cone[1] = 15;
2452         cone[2] = 16;
2453         cone[3] = 17;
2454         PetscCall(DMPlexSetCone(dm, 0, cone));
2455         ornt[0] = 0;
2456         ornt[1] = 0;
2457         ornt[2] = 0;
2458         ornt[3] = 0;
2459         PetscCall(DMPlexSetConeOrientation(dm, 0, ornt));
2460         /* Cell 1 */
2461         cone[0] = 18;
2462         cone[1] = 19;
2463         cone[2] = 14;
2464         cone[3] = 20;
2465         PetscCall(DMPlexSetCone(dm, 1, cone));
2466         ornt[0] = 0;
2467         ornt[1] = 0;
2468         ornt[2] = -1;
2469         ornt[3] = 0;
2470         PetscCall(DMPlexSetConeOrientation(dm, 1, ornt));
2471         /* Cell 2 */
2472         cone[0] = 21;
2473         cone[1] = 22;
2474         cone[2] = 18;
2475         cone[3] = 23;
2476         PetscCall(DMPlexSetCone(dm, 2, cone));
2477         ornt[0] = 0;
2478         ornt[1] = 0;
2479         ornt[2] = -1;
2480         ornt[3] = 0;
2481         PetscCall(DMPlexSetConeOrientation(dm, 2, ornt));
2482         /* Cell 3 */
2483         cone[0] = 19;
2484         cone[1] = 22;
2485         cone[2] = 24;
2486         cone[3] = 15;
2487         PetscCall(DMPlexSetCone(dm, 3, cone));
2488         ornt[0] = -1;
2489         ornt[1] = -1;
2490         ornt[2] = 0;
2491         ornt[3] = -1;
2492         PetscCall(DMPlexSetConeOrientation(dm, 3, ornt));
2493         /* Cell 4 */
2494         cone[0] = 16;
2495         cone[1] = 24;
2496         cone[2] = 21;
2497         cone[3] = 25;
2498         PetscCall(DMPlexSetCone(dm, 4, cone));
2499         ornt[0] = -1;
2500         ornt[1] = -1;
2501         ornt[2] = -1;
2502         ornt[3] = 0;
2503         PetscCall(DMPlexSetConeOrientation(dm, 4, ornt));
2504         /* Cell 5 */
2505         cone[0] = 20;
2506         cone[1] = 17;
2507         cone[2] = 25;
2508         cone[3] = 23;
2509         PetscCall(DMPlexSetCone(dm, 5, cone));
2510         ornt[0] = -1;
2511         ornt[1] = -1;
2512         ornt[2] = -1;
2513         ornt[3] = -1;
2514         PetscCall(DMPlexSetConeOrientation(dm, 5, ornt));
2515         /* Edges */
2516         cone[0] = 6;
2517         cone[1] = 7;
2518         PetscCall(DMPlexSetCone(dm, 14, cone));
2519         cone[0] = 7;
2520         cone[1] = 8;
2521         PetscCall(DMPlexSetCone(dm, 15, cone));
2522         cone[0] = 8;
2523         cone[1] = 9;
2524         PetscCall(DMPlexSetCone(dm, 16, cone));
2525         cone[0] = 9;
2526         cone[1] = 6;
2527         PetscCall(DMPlexSetCone(dm, 17, cone));
2528         cone[0] = 10;
2529         cone[1] = 11;
2530         PetscCall(DMPlexSetCone(dm, 18, cone));
2531         cone[0] = 11;
2532         cone[1] = 7;
2533         PetscCall(DMPlexSetCone(dm, 19, cone));
2534         cone[0] = 6;
2535         cone[1] = 10;
2536         PetscCall(DMPlexSetCone(dm, 20, cone));
2537         cone[0] = 12;
2538         cone[1] = 13;
2539         PetscCall(DMPlexSetCone(dm, 21, cone));
2540         cone[0] = 13;
2541         cone[1] = 11;
2542         PetscCall(DMPlexSetCone(dm, 22, cone));
2543         cone[0] = 10;
2544         cone[1] = 12;
2545         PetscCall(DMPlexSetCone(dm, 23, cone));
2546         cone[0] = 13;
2547         cone[1] = 8;
2548         PetscCall(DMPlexSetCone(dm, 24, cone));
2549         cone[0] = 12;
2550         cone[1] = 9;
2551         PetscCall(DMPlexSetCone(dm, 25, cone));
2552       }
2553       PetscCall(DMPlexSymmetrize(dm));
2554       PetscCall(DMPlexStratify(dm));
2555       /* Build coordinates */
2556       PetscCall(PetscCalloc1(numVerts * embedDim, &coordsIn));
2557       if (rank == 0) {
2558         coordsIn[0 * embedDim + 0] = -R;
2559         coordsIn[0 * embedDim + 1] = R;
2560         coordsIn[0 * embedDim + 2] = -R;
2561         coordsIn[1 * embedDim + 0] = R;
2562         coordsIn[1 * embedDim + 1] = R;
2563         coordsIn[1 * embedDim + 2] = -R;
2564         coordsIn[2 * embedDim + 0] = R;
2565         coordsIn[2 * embedDim + 1] = -R;
2566         coordsIn[2 * embedDim + 2] = -R;
2567         coordsIn[3 * embedDim + 0] = -R;
2568         coordsIn[3 * embedDim + 1] = -R;
2569         coordsIn[3 * embedDim + 2] = -R;
2570         coordsIn[4 * embedDim + 0] = -R;
2571         coordsIn[4 * embedDim + 1] = R;
2572         coordsIn[4 * embedDim + 2] = R;
2573         coordsIn[5 * embedDim + 0] = R;
2574         coordsIn[5 * embedDim + 1] = R;
2575         coordsIn[5 * embedDim + 2] = R;
2576         coordsIn[6 * embedDim + 0] = -R;
2577         coordsIn[6 * embedDim + 1] = -R;
2578         coordsIn[6 * embedDim + 2] = R;
2579         coordsIn[7 * embedDim + 0] = R;
2580         coordsIn[7 * embedDim + 1] = -R;
2581         coordsIn[7 * embedDim + 2] = R;
2582       }
2583     }
2584     break;
2585   case 3:
2586     if (simplex) {
2587       const PetscReal edgeLen         = 1.0 / PETSC_PHI;
2588       PetscReal       vertexA[4]      = {0.5, 0.5, 0.5, 0.5};
2589       PetscReal       vertexB[4]      = {1.0, 0.0, 0.0, 0.0};
2590       PetscReal       vertexC[4]      = {0.5, 0.5 * PETSC_PHI, 0.5 / PETSC_PHI, 0.0};
2591       const PetscInt  degree          = 12;
2592       PetscInt        s[4]            = {1, 1, 1};
2593       PetscInt        evenPerm[12][4] = {
2594         {0, 1, 2, 3},
2595         {0, 2, 3, 1},
2596         {0, 3, 1, 2},
2597         {1, 0, 3, 2},
2598         {1, 2, 0, 3},
2599         {1, 3, 2, 0},
2600         {2, 0, 1, 3},
2601         {2, 1, 3, 0},
2602         {2, 3, 0, 1},
2603         {3, 0, 2, 1},
2604         {3, 1, 0, 2},
2605         {3, 2, 1, 0}
2606       };
2607       PetscInt  cone[4];
2608       PetscInt *graph, p, i, j, k, l;
2609 
2610       vertexA[0] *= R;
2611       vertexA[1] *= R;
2612       vertexA[2] *= R;
2613       vertexA[3] *= R;
2614       vertexB[0] *= R;
2615       vertexB[1] *= R;
2616       vertexB[2] *= R;
2617       vertexB[3] *= R;
2618       vertexC[0] *= R;
2619       vertexC[1] *= R;
2620       vertexC[2] *= R;
2621       vertexC[3] *= R;
2622       numCells    = rank == 0 ? 600 : 0;
2623       numVerts    = rank == 0 ? 120 : 0;
2624       firstVertex = numCells;
2625       /* Use the 600-cell, which for a unit sphere has coordinates which are
2626 
2627            1/2 (\pm 1, \pm 1,    \pm 1, \pm 1)                          16
2628                (\pm 1,    0,       0,      0)  all cyclic permutations   8
2629            1/2 (\pm 1, \pm phi, \pm 1/phi, 0)  all even permutations    96
2630 
2631          where \phi^2 - \phi - 1 = 0, meaning \phi is the golden ratio \frac{1 + \sqrt{5}}{2}. The edge
2632          length is then given by 1/\phi = 0.61803.
2633 
2634          http://buzzard.pugetsound.edu/sage-practice/ch03s03.html
2635          http://mathworld.wolfram.com/600-Cell.html
2636       */
2637       /* Construct vertices */
2638       PetscCall(PetscCalloc1(numVerts * embedDim, &coordsIn));
2639       i = 0;
2640       if (rank == 0) {
2641         for (s[0] = -1; s[0] < 2; s[0] += 2) {
2642           for (s[1] = -1; s[1] < 2; s[1] += 2) {
2643             for (s[2] = -1; s[2] < 2; s[2] += 2) {
2644               for (s[3] = -1; s[3] < 2; s[3] += 2) {
2645                 for (d = 0; d < embedDim; ++d) coordsIn[i * embedDim + d] = s[d] * vertexA[d];
2646                 ++i;
2647               }
2648             }
2649           }
2650         }
2651         for (p = 0; p < embedDim; ++p) {
2652           s[1] = s[2] = s[3] = 1;
2653           for (s[0] = -1; s[0] < 2; s[0] += 2) {
2654             for (d = 0; d < embedDim; ++d) coordsIn[i * embedDim + d] = s[(d + p) % embedDim] * vertexB[(d + p) % embedDim];
2655             ++i;
2656           }
2657         }
2658         for (p = 0; p < 12; ++p) {
2659           s[3] = 1;
2660           for (s[0] = -1; s[0] < 2; s[0] += 2) {
2661             for (s[1] = -1; s[1] < 2; s[1] += 2) {
2662               for (s[2] = -1; s[2] < 2; s[2] += 2) {
2663                 for (d = 0; d < embedDim; ++d) coordsIn[i * embedDim + d] = s[evenPerm[p][d]] * vertexC[evenPerm[p][d]];
2664                 ++i;
2665               }
2666             }
2667           }
2668         }
2669       }
2670       PetscCheck(i == numVerts, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid 600-cell, vertices %" PetscInt_FMT " != %" PetscInt_FMT, i, numVerts);
2671       /* Construct graph */
2672       PetscCall(PetscCalloc1(numVerts * numVerts, &graph));
2673       for (i = 0; i < numVerts; ++i) {
2674         for (j = 0, k = 0; j < numVerts; ++j) {
2675           if (PetscAbsReal(DiffNormReal(embedDim, &coordsIn[i * embedDim], &coordsIn[j * embedDim]) - edgeLen) < PETSC_SMALL) {
2676             graph[i * numVerts + j] = 1;
2677             ++k;
2678           }
2679         }
2680         PetscCheck(k == degree, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid 600-cell, vertex %" PetscInt_FMT " degree %" PetscInt_FMT " != %" PetscInt_FMT, i, k, degree);
2681       }
2682       /* Build Topology */
2683       PetscCall(DMPlexSetChart(dm, 0, numCells + numVerts));
2684       for (c = 0; c < numCells; c++) PetscCall(DMPlexSetConeSize(dm, c, embedDim));
2685       PetscCall(DMSetUp(dm)); /* Allocate space for cones */
2686       /* Cells */
2687       if (rank == 0) {
2688         for (i = 0, c = 0; i < numVerts; ++i) {
2689           for (j = 0; j < i; ++j) {
2690             for (k = 0; k < j; ++k) {
2691               for (l = 0; l < k; ++l) {
2692                 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]) {
2693                   cone[0] = firstVertex + i;
2694                   cone[1] = firstVertex + j;
2695                   cone[2] = firstVertex + k;
2696                   cone[3] = firstVertex + l;
2697                   /* Check orientation: https://ef.gy/linear-algebra:normal-vectors-in-higher-dimensional-spaces */
2698                   {
2699                     const PetscInt epsilon[4][4][4][4] = {
2700                       {{{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}}},
2701 
2702                       {{{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}}},
2703 
2704                       {{{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}}},
2705 
2706                       {{{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}} }
2707                     };
2708                     PetscReal normal[4];
2709                     PetscInt  e, f, g;
2710 
2711                     for (d = 0; d < embedDim; ++d) {
2712                       normal[d] = 0.0;
2713                       for (e = 0; e < embedDim; ++e) {
2714                         for (f = 0; f < embedDim; ++f) {
2715                           for (g = 0; g < embedDim; ++g) {
2716                             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]);
2717                           }
2718                         }
2719                       }
2720                     }
2721                     if (DotReal(embedDim, normal, &coordsIn[i * embedDim]) < 0) {
2722                       PetscInt tmp = cone[1];
2723                       cone[1]      = cone[2];
2724                       cone[2]      = tmp;
2725                     }
2726                   }
2727                   PetscCall(DMPlexSetCone(dm, c++, cone));
2728                 }
2729               }
2730             }
2731           }
2732         }
2733       }
2734       PetscCall(DMPlexSymmetrize(dm));
2735       PetscCall(DMPlexStratify(dm));
2736       PetscCall(PetscFree(graph));
2737     }
2738     break;
2739   default:
2740     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Unsupported dimension for sphere: %" PetscInt_FMT, dim);
2741   }
2742   /* Create coordinates */
2743   PetscCall(DMGetCoordinateSection(dm, &coordSection));
2744   PetscCall(PetscSectionSetNumFields(coordSection, 1));
2745   PetscCall(PetscSectionSetFieldComponents(coordSection, 0, embedDim));
2746   PetscCall(PetscSectionSetChart(coordSection, firstVertex, firstVertex + numVerts));
2747   for (v = firstVertex; v < firstVertex + numVerts; ++v) {
2748     PetscCall(PetscSectionSetDof(coordSection, v, embedDim));
2749     PetscCall(PetscSectionSetFieldDof(coordSection, v, 0, embedDim));
2750   }
2751   PetscCall(PetscSectionSetUp(coordSection));
2752   PetscCall(PetscSectionGetStorageSize(coordSection, &coordSize));
2753   PetscCall(VecCreate(PETSC_COMM_SELF, &coordinates));
2754   PetscCall(VecSetBlockSize(coordinates, embedDim));
2755   PetscCall(PetscObjectSetName((PetscObject)coordinates, "coordinates"));
2756   PetscCall(VecSetSizes(coordinates, coordSize, PETSC_DETERMINE));
2757   PetscCall(VecSetType(coordinates, VECSTANDARD));
2758   PetscCall(VecGetArray(coordinates, &coords));
2759   for (v = 0; v < numVerts; ++v)
2760     for (d = 0; d < embedDim; ++d) coords[v * embedDim + d] = coordsIn[v * embedDim + d];
2761   PetscCall(VecRestoreArray(coordinates, &coords));
2762   PetscCall(DMSetCoordinatesLocal(dm, coordinates));
2763   PetscCall(VecDestroy(&coordinates));
2764   PetscCall(PetscFree(coordsIn));
2765   {
2766     DM          cdm;
2767     PetscDS     cds;
2768     PetscScalar c = R;
2769 
2770     PetscCall(DMPlexCreateCoordinateSpace(dm, 1, snapToSphere));
2771     PetscCall(DMGetCoordinateDM(dm, &cdm));
2772     PetscCall(DMGetDS(cdm, &cds));
2773     PetscCall(PetscDSSetConstants(cds, 1, &c));
2774   }
2775   /* Wait for coordinate creation before doing in-place modification */
2776   if (simplex) PetscCall(DMPlexInterpolateInPlace_Internal(dm));
2777   PetscFunctionReturn(0);
2778 }
2779 
2780 typedef void (*TPSEvaluateFunc)(const PetscReal[], PetscReal *, PetscReal[], PetscReal (*)[3]);
2781 
2782 /*
2783  The Schwarz P implicit surface is
2784 
2785      f(x) = cos(x0) + cos(x1) + cos(x2) = 0
2786 */
2787 static void TPSEvaluate_SchwarzP(const PetscReal y[3], PetscReal *f, PetscReal grad[], PetscReal (*hess)[3])
2788 {
2789   PetscReal c[3] = {PetscCosReal(y[0] * PETSC_PI), PetscCosReal(y[1] * PETSC_PI), PetscCosReal(y[2] * PETSC_PI)};
2790   PetscReal g[3] = {-PetscSinReal(y[0] * PETSC_PI), -PetscSinReal(y[1] * PETSC_PI), -PetscSinReal(y[2] * PETSC_PI)};
2791   f[0]           = c[0] + c[1] + c[2];
2792   for (PetscInt i = 0; i < 3; i++) {
2793     grad[i] = PETSC_PI * g[i];
2794     for (PetscInt j = 0; j < 3; j++) hess[i][j] = (i == j) ? -PetscSqr(PETSC_PI) * c[i] : 0.;
2795   }
2796 }
2797 
2798 // u[] is a tentative normal on input. Replace with the implicit function gradient in the same direction
2799 static PetscErrorCode TPSExtrudeNormalFunc_SchwarzP(PetscInt dim, PetscReal time, const PetscReal x[], PetscInt r, PetscScalar u[], void *ctx)
2800 {
2801   for (PetscInt i = 0; i < 3; i++) u[i] = -PETSC_PI * PetscSinReal(x[i] * PETSC_PI);
2802   return 0;
2803 }
2804 
2805 /*
2806  The Gyroid implicit surface is
2807 
2808  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)
2809 
2810 */
2811 static void TPSEvaluate_Gyroid(const PetscReal y[3], PetscReal *f, PetscReal grad[], PetscReal (*hess)[3])
2812 {
2813   PetscReal s[3] = {PetscSinReal(PETSC_PI * y[0]), PetscSinReal(PETSC_PI * (y[1] + .5)), PetscSinReal(PETSC_PI * (y[2] + .25))};
2814   PetscReal c[3] = {PetscCosReal(PETSC_PI * y[0]), PetscCosReal(PETSC_PI * (y[1] + .5)), PetscCosReal(PETSC_PI * (y[2] + .25))};
2815   f[0]           = s[0] * c[1] + s[1] * c[2] + s[2] * c[0];
2816   grad[0]        = PETSC_PI * (c[0] * c[1] - s[2] * s[0]);
2817   grad[1]        = PETSC_PI * (c[1] * c[2] - s[0] * s[1]);
2818   grad[2]        = PETSC_PI * (c[2] * c[0] - s[1] * s[2]);
2819   hess[0][0]     = -PetscSqr(PETSC_PI) * (s[0] * c[1] + s[2] * c[0]);
2820   hess[0][1]     = -PetscSqr(PETSC_PI) * (c[0] * s[1]);
2821   hess[0][2]     = -PetscSqr(PETSC_PI) * (c[2] * s[0]);
2822   hess[1][0]     = -PetscSqr(PETSC_PI) * (s[1] * c[2] + s[0] * c[1]);
2823   hess[1][1]     = -PetscSqr(PETSC_PI) * (c[1] * s[2]);
2824   hess[2][2]     = -PetscSqr(PETSC_PI) * (c[0] * s[1]);
2825   hess[2][0]     = -PetscSqr(PETSC_PI) * (s[2] * c[0] + s[1] * c[2]);
2826   hess[2][1]     = -PetscSqr(PETSC_PI) * (c[2] * s[0]);
2827   hess[2][2]     = -PetscSqr(PETSC_PI) * (c[1] * s[2]);
2828 }
2829 
2830 // u[] is a tentative normal on input. Replace with the implicit function gradient in the same direction
2831 static PetscErrorCode TPSExtrudeNormalFunc_Gyroid(PetscInt dim, PetscReal time, const PetscReal x[], PetscInt r, PetscScalar u[], void *ctx)
2832 {
2833   PetscReal s[3] = {PetscSinReal(PETSC_PI * x[0]), PetscSinReal(PETSC_PI * (x[1] + .5)), PetscSinReal(PETSC_PI * (x[2] + .25))};
2834   PetscReal c[3] = {PetscCosReal(PETSC_PI * x[0]), PetscCosReal(PETSC_PI * (x[1] + .5)), PetscCosReal(PETSC_PI * (x[2] + .25))};
2835   u[0]           = PETSC_PI * (c[0] * c[1] - s[2] * s[0]);
2836   u[1]           = PETSC_PI * (c[1] * c[2] - s[0] * s[1]);
2837   u[2]           = PETSC_PI * (c[2] * c[0] - s[1] * s[2]);
2838   return 0;
2839 }
2840 
2841 /*
2842    We wish to solve
2843 
2844          min_y || y - x ||^2  subject to f(y) = 0
2845 
2846    Let g(y) = grad(f).  The minimization problem is equivalent to asking to satisfy
2847    f(y) = 0 and (y-x) is parallel to g(y).  We do this by using Householder QR to obtain a basis for the
2848    tangent space and ask for both components in the tangent space to be zero.
2849 
2850    Take g to be a column vector and compute the "full QR" factorization Q R = g,
2851    where Q = I - 2 n n^T is a symmetric orthogonal matrix.
2852    The first column of Q is parallel to g so the remaining two columns span the null space.
2853    Let Qn = Q[:,1:] be those remaining columns.  Then Qn Qn^T is an orthogonal projector into the tangent space.
2854    Since Q is symmetric, this is equivalent to multipyling by Q and taking the last two entries.
2855    In total, we have a system of 3 equations in 3 unknowns:
2856 
2857      f(y) = 0                       1 equation
2858      Qn^T (y - x) = 0               2 equations
2859 
2860    Here, we compute the residual and Jacobian of this system.
2861 */
2862 static void TPSNearestPointResJac(TPSEvaluateFunc feval, const PetscScalar x[], const PetscScalar y[], PetscScalar res[], PetscScalar J[])
2863 {
2864   PetscReal yreal[3] = {PetscRealPart(y[0]), PetscRealPart(y[1]), PetscRealPart(y[2])};
2865   PetscReal d[3]     = {PetscRealPart(y[0] - x[0]), PetscRealPart(y[1] - x[1]), PetscRealPart(y[2] - x[2])};
2866   PetscReal f, grad[3], n[3], norm, norm_y[3], nd, nd_y[3], sign;
2867   PetscReal n_y[3][3] = {
2868     {0, 0, 0},
2869     {0, 0, 0},
2870     {0, 0, 0}
2871   };
2872 
2873   feval(yreal, &f, grad, n_y);
2874 
2875   for (PetscInt i = 0; i < 3; i++) n[i] = grad[i];
2876   norm = PetscSqrtReal(PetscSqr(n[0]) + PetscSqr(n[1]) + PetscSqr(n[2]));
2877   for (PetscInt i = 0; i < 3; i++) norm_y[i] = 1. / norm * n[i] * n_y[i][i];
2878 
2879   // Define the Householder reflector
2880   sign = n[0] >= 0 ? 1. : -1.;
2881   n[0] += norm * sign;
2882   for (PetscInt i = 0; i < 3; i++) n_y[0][i] += norm_y[i] * sign;
2883 
2884   norm      = PetscSqrtReal(PetscSqr(n[0]) + PetscSqr(n[1]) + PetscSqr(n[2]));
2885   norm_y[0] = 1. / norm * (n[0] * n_y[0][0]);
2886   norm_y[1] = 1. / norm * (n[0] * n_y[0][1] + n[1] * n_y[1][1]);
2887   norm_y[2] = 1. / norm * (n[0] * n_y[0][2] + n[2] * n_y[2][2]);
2888 
2889   for (PetscInt i = 0; i < 3; i++) {
2890     n[i] /= norm;
2891     for (PetscInt j = 0; j < 3; j++) {
2892       // note that n[i] is n_old[i]/norm when executing the code below
2893       n_y[i][j] = n_y[i][j] / norm - n[i] / norm * norm_y[j];
2894     }
2895   }
2896 
2897   nd = n[0] * d[0] + n[1] * d[1] + n[2] * d[2];
2898   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];
2899 
2900   res[0] = f;
2901   res[1] = d[1] - 2 * n[1] * nd;
2902   res[2] = d[2] - 2 * n[2] * nd;
2903   // J[j][i] is J_{ij} (column major)
2904   for (PetscInt j = 0; j < 3; j++) {
2905     J[0 + j * 3] = grad[j];
2906     J[1 + j * 3] = (j == 1) * 1. - 2 * (n_y[1][j] * nd + n[1] * nd_y[j]);
2907     J[2 + j * 3] = (j == 2) * 1. - 2 * (n_y[2][j] * nd + n[2] * nd_y[j]);
2908   }
2909 }
2910 
2911 /*
2912    Project x to the nearest point on the implicit surface using Newton's method.
2913 */
2914 static PetscErrorCode TPSNearestPoint(TPSEvaluateFunc feval, PetscScalar x[])
2915 {
2916   PetscScalar y[3] = {x[0], x[1], x[2]}; // Initial guess
2917 
2918   PetscFunctionBegin;
2919   for (PetscInt iter = 0; iter < 10; iter++) {
2920     PetscScalar res[3], J[9];
2921     PetscReal   resnorm;
2922     TPSNearestPointResJac(feval, x, y, res, J);
2923     resnorm = PetscSqrtReal(PetscSqr(PetscRealPart(res[0])) + PetscSqr(PetscRealPart(res[1])) + PetscSqr(PetscRealPart(res[2])));
2924     if (0) { // Turn on this monitor if you need to confirm quadratic convergence
2925       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])));
2926     }
2927     if (resnorm < PETSC_SMALL) break;
2928 
2929     // Take the Newton step
2930     PetscCall(PetscKernel_A_gets_inverse_A_3(J, 0., PETSC_FALSE, NULL));
2931     PetscKernel_v_gets_v_minus_A_times_w_3(y, J, res);
2932   }
2933   for (PetscInt i = 0; i < 3; i++) x[i] = y[i];
2934   PetscFunctionReturn(0);
2935 }
2936 
2937 const char *const DMPlexTPSTypes[] = {"SCHWARZ_P", "GYROID", "DMPlexTPSType", "DMPLEX_TPS_", NULL};
2938 
2939 static PetscErrorCode DMPlexCreateTPSMesh_Internal(DM dm, DMPlexTPSType tpstype, const PetscInt extent[], const DMBoundaryType periodic[], PetscBool tps_distribute, PetscInt refinements, PetscInt layers, PetscReal thickness)
2940 {
2941   PetscMPIInt rank;
2942   PetscInt    topoDim = 2, spaceDim = 3, numFaces = 0, numVertices = 0, numEdges = 0;
2943   PetscInt(*edges)[2] = NULL, *edgeSets = NULL;
2944   PetscInt            *cells_flat = NULL;
2945   PetscReal           *vtxCoords  = NULL;
2946   TPSEvaluateFunc      evalFunc   = NULL;
2947   PetscSimplePointFunc normalFunc = NULL;
2948   DMLabel              label;
2949 
2950   PetscFunctionBegin;
2951   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
2952   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);
2953   switch (tpstype) {
2954   case DMPLEX_TPS_SCHWARZ_P:
2955     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");
2956     if (rank == 0) {
2957       PetscInt(*cells)[6][4][4] = NULL; // [junction, junction-face, cell, conn]
2958       PetscInt  Njunctions = 0, Ncuts = 0, Npipes[3], vcount;
2959       PetscReal L = 1;
2960 
2961       Npipes[0]   = (extent[0] + 1) * extent[1] * extent[2];
2962       Npipes[1]   = extent[0] * (extent[1] + 1) * extent[2];
2963       Npipes[2]   = extent[0] * extent[1] * (extent[2] + 1);
2964       Njunctions  = extent[0] * extent[1] * extent[2];
2965       Ncuts       = 2 * (extent[0] * extent[1] + extent[1] * extent[2] + extent[2] * extent[0]);
2966       numVertices = 4 * (Npipes[0] + Npipes[1] + Npipes[2]) + 8 * Njunctions;
2967       PetscCall(PetscMalloc1(3 * numVertices, &vtxCoords));
2968       PetscCall(PetscMalloc1(Njunctions, &cells));
2969       PetscCall(PetscMalloc1(Ncuts * 4, &edges));
2970       PetscCall(PetscMalloc1(Ncuts * 4, &edgeSets));
2971       // x-normal pipes
2972       vcount = 0;
2973       for (PetscInt i = 0; i < extent[0] + 1; i++) {
2974         for (PetscInt j = 0; j < extent[1]; j++) {
2975           for (PetscInt k = 0; k < extent[2]; k++) {
2976             for (PetscInt l = 0; l < 4; l++) {
2977               vtxCoords[vcount++] = (2 * i - 1) * L;
2978               vtxCoords[vcount++] = 2 * j * L + PetscCosReal((2 * l + 1) * PETSC_PI / 4) * L / 2;
2979               vtxCoords[vcount++] = 2 * k * L + PetscSinReal((2 * l + 1) * PETSC_PI / 4) * L / 2;
2980             }
2981           }
2982         }
2983       }
2984       // y-normal pipes
2985       for (PetscInt i = 0; i < extent[0]; i++) {
2986         for (PetscInt j = 0; j < extent[1] + 1; j++) {
2987           for (PetscInt k = 0; k < extent[2]; k++) {
2988             for (PetscInt l = 0; l < 4; l++) {
2989               vtxCoords[vcount++] = 2 * i * L + PetscSinReal((2 * l + 1) * PETSC_PI / 4) * L / 2;
2990               vtxCoords[vcount++] = (2 * j - 1) * L;
2991               vtxCoords[vcount++] = 2 * k * L + PetscCosReal((2 * l + 1) * PETSC_PI / 4) * L / 2;
2992             }
2993           }
2994         }
2995       }
2996       // z-normal pipes
2997       for (PetscInt i = 0; i < extent[0]; i++) {
2998         for (PetscInt j = 0; j < extent[1]; j++) {
2999           for (PetscInt k = 0; k < extent[2] + 1; k++) {
3000             for (PetscInt l = 0; l < 4; l++) {
3001               vtxCoords[vcount++] = 2 * i * L + PetscCosReal((2 * l + 1) * PETSC_PI / 4) * L / 2;
3002               vtxCoords[vcount++] = 2 * j * L + PetscSinReal((2 * l + 1) * PETSC_PI / 4) * L / 2;
3003               vtxCoords[vcount++] = (2 * k - 1) * L;
3004             }
3005           }
3006         }
3007       }
3008       // junctions
3009       for (PetscInt i = 0; i < extent[0]; i++) {
3010         for (PetscInt j = 0; j < extent[1]; j++) {
3011           for (PetscInt k = 0; k < extent[2]; k++) {
3012             const PetscInt J = (i * extent[1] + j) * extent[2] + k, Jvoff = (Npipes[0] + Npipes[1] + Npipes[2]) * 4 + J * 8;
3013             PetscCheck(vcount / 3 == Jvoff, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected vertex count");
3014             for (PetscInt ii = 0; ii < 2; ii++) {
3015               for (PetscInt jj = 0; jj < 2; jj++) {
3016                 for (PetscInt kk = 0; kk < 2; kk++) {
3017                   double Ls           = (1 - sqrt(2) / 4) * L;
3018                   vtxCoords[vcount++] = 2 * i * L + (2 * ii - 1) * Ls;
3019                   vtxCoords[vcount++] = 2 * j * L + (2 * jj - 1) * Ls;
3020                   vtxCoords[vcount++] = 2 * k * L + (2 * kk - 1) * Ls;
3021                 }
3022               }
3023             }
3024             const PetscInt jfaces[3][2][4] = {
3025               {{3, 1, 0, 2}, {7, 5, 4, 6}}, // x-aligned
3026               {{5, 4, 0, 1}, {7, 6, 2, 3}}, // y-aligned
3027               {{6, 2, 0, 4}, {7, 3, 1, 5}}  // z-aligned
3028             };
3029             const PetscInt pipe_lo[3] = {// vertex numbers of pipes
3030                                          ((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};
3031             const PetscInt pipe_hi[3] = {// vertex numbers of pipes
3032                                          (((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};
3033             for (PetscInt dir = 0; dir < 3; dir++) { // x,y,z
3034               const PetscInt ijk[3] = {i, j, k};
3035               for (PetscInt l = 0; l < 4; l++) { // rotations
3036                 cells[J][dir * 2 + 0][l][0] = pipe_lo[dir] + l;
3037                 cells[J][dir * 2 + 0][l][1] = Jvoff + jfaces[dir][0][l];
3038                 cells[J][dir * 2 + 0][l][2] = Jvoff + jfaces[dir][0][(l - 1 + 4) % 4];
3039                 cells[J][dir * 2 + 0][l][3] = pipe_lo[dir] + (l - 1 + 4) % 4;
3040                 cells[J][dir * 2 + 1][l][0] = Jvoff + jfaces[dir][1][l];
3041                 cells[J][dir * 2 + 1][l][1] = pipe_hi[dir] + l;
3042                 cells[J][dir * 2 + 1][l][2] = pipe_hi[dir] + (l - 1 + 4) % 4;
3043                 cells[J][dir * 2 + 1][l][3] = Jvoff + jfaces[dir][1][(l - 1 + 4) % 4];
3044                 if (ijk[dir] == 0) {
3045                   edges[numEdges][0] = pipe_lo[dir] + l;
3046                   edges[numEdges][1] = pipe_lo[dir] + (l + 1) % 4;
3047                   edgeSets[numEdges] = dir * 2 + 1;
3048                   numEdges++;
3049                 }
3050                 if (ijk[dir] + 1 == extent[dir]) {
3051                   edges[numEdges][0] = pipe_hi[dir] + l;
3052                   edges[numEdges][1] = pipe_hi[dir] + (l + 1) % 4;
3053                   edgeSets[numEdges] = dir * 2 + 2;
3054                   numEdges++;
3055                 }
3056               }
3057             }
3058           }
3059         }
3060       }
3061       PetscCheck(numEdges == Ncuts * 4, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Edge count %" PetscInt_FMT " incompatible with number of cuts %" PetscInt_FMT, numEdges, Ncuts);
3062       numFaces   = 24 * Njunctions;
3063       cells_flat = cells[0][0][0];
3064     }
3065     evalFunc   = TPSEvaluate_SchwarzP;
3066     normalFunc = TPSExtrudeNormalFunc_SchwarzP;
3067     break;
3068   case DMPLEX_TPS_GYROID:
3069     if (rank == 0) {
3070       // This is a coarse mesh approximation of the gyroid shifted to being the zero of the level set
3071       //
3072       //     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)
3073       //
3074       // on the cell [0,2]^3.
3075       //
3076       // Think about dividing that cell into four columns, and focus on the column [0,1]x[0,1]x[0,2].
3077       // If you looked at the gyroid in that column at different slices of z you would see that it kind of spins
3078       // like a boomerang:
3079       //
3080       //     z = 0          z = 1/4        z = 1/2        z = 3/4     //
3081       //     -----          -------        -------        -------     //
3082       //                                                              //
3083       //     +       +      +       +      +       +      +   \   +   //
3084       //      \                                   /            \      //
3085       //       \            `-_   _-'            /              }     //
3086       //        *-_            `-'            _-'              /      //
3087       //     +     `-+      +       +      +-'     +      +   /   +   //
3088       //                                                              //
3089       //                                                              //
3090       //     z = 1          z = 5/4        z = 3/2        z = 7/4     //
3091       //     -----          -------        -------        -------     //
3092       //                                                              //
3093       //     +-_     +      +       +      +     _-+      +   /   +   //
3094       //        `-_            _-_            _-`            /        //
3095       //           \        _-'   `-_        /              {         //
3096       //            \                       /                \        //
3097       //     +       +      +       +      +       +      +   \   +   //
3098       //
3099       //
3100       // This course mesh approximates each of these slices by two line segments,
3101       // and then connects the segments in consecutive layers with quadrilateral faces.
3102       // All of the end points of the segments are multiples of 1/4 except for the
3103       // point * in the picture for z = 0 above and the similar points in other layers.
3104       // That point is at (gamma, gamma, 0), where gamma is calculated below.
3105       //
3106       // The column  [1,2]x[1,2]x[0,2] looks the same as this column;
3107       // The columns [1,2]x[0,1]x[0,2] and [0,1]x[1,2]x[0,2] are mirror images.
3108       //
3109       // As for how this method turned into the names given to the vertices:
3110       // that was not systematic, it was just the way it worked out in my handwritten notes.
3111 
3112       PetscInt facesPerBlock = 64;
3113       PetscInt vertsPerBlock = 56;
3114       PetscInt extentPlus[3];
3115       PetscInt numBlocks, numBlocksPlus;
3116       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;
3117       const PetscInt pattern[64][4] = {
3118   /* face to vertex within the coarse discretization of a single gyroid block */
3119   /* layer 0 */
3120         {A,           C,           K,           G          },
3121         {C,           B,           II,          K          },
3122         {D,           A,           H,           L          },
3123         {B + 56 * 1,  D,           L,           J          },
3124         {E,           B + 56 * 1,  J,           N          },
3125         {A + 56 * 2,  E,           N,           H + 56 * 2 },
3126         {F,           A + 56 * 2,  G + 56 * 2,  M          },
3127         {B,           F,           M,           II         },
3128  /* layer 1 */
3129         {G,           K,           Q,           O          },
3130         {K,           II,          P,           Q          },
3131         {L,           H,           O + 56 * 1,  R          },
3132         {J,           L,           R,           P          },
3133         {N,           J,           P,           S          },
3134         {H + 56 * 2,  N,           S,           O + 56 * 3 },
3135         {M,           G + 56 * 2,  O + 56 * 2,  T          },
3136         {II,          M,           T,           P          },
3137  /* layer 2 */
3138         {O,           Q,           Y,           U          },
3139         {Q,           P,           W,           Y          },
3140         {R,           O + 56 * 1,  U + 56 * 1,  Ap         },
3141         {P,           R,           Ap,          W          },
3142         {S,           P,           X,           Bp         },
3143         {O + 56 * 3,  S,           Bp,          V + 56 * 1 },
3144         {T,           O + 56 * 2,  V,           Z          },
3145         {P,           T,           Z,           X          },
3146  /* layer 3 */
3147         {U,           Y,           Ep,          Dp         },
3148         {Y,           W,           Cp,          Ep         },
3149         {Ap,          U + 56 * 1,  Dp + 56 * 1, Gp         },
3150         {W,           Ap,          Gp,          Cp         },
3151         {Bp,          X,           Cp + 56 * 2, Fp         },
3152         {V + 56 * 1,  Bp,          Fp,          Dp + 56 * 1},
3153         {Z,           V,           Dp,          Hp         },
3154         {X,           Z,           Hp,          Cp + 56 * 2},
3155  /* layer 4 */
3156         {Dp,          Ep,          Mp,          Kp         },
3157         {Ep,          Cp,          Ip,          Mp         },
3158         {Gp,          Dp + 56 * 1, Lp,          Np         },
3159         {Cp,          Gp,          Np,          Jp         },
3160         {Fp,          Cp + 56 * 2, Jp + 56 * 2, Pp         },
3161         {Dp + 56 * 1, Fp,          Pp,          Lp         },
3162         {Hp,          Dp,          Kp,          Op         },
3163         {Cp + 56 * 2, Hp,          Op,          Ip + 56 * 2},
3164  /* layer 5 */
3165         {Kp,          Mp,          Sp,          Rp         },
3166         {Mp,          Ip,          Qp,          Sp         },
3167         {Np,          Lp,          Rp,          Tp         },
3168         {Jp,          Np,          Tp,          Qp + 56 * 1},
3169         {Pp,          Jp + 56 * 2, Qp + 56 * 3, Up         },
3170         {Lp,          Pp,          Up,          Rp         },
3171         {Op,          Kp,          Rp,          Vp         },
3172         {Ip + 56 * 2, Op,          Vp,          Qp + 56 * 2},
3173  /* layer 6 */
3174         {Rp,          Sp,          Aq,          Yp         },
3175         {Sp,          Qp,          Wp,          Aq         },
3176         {Tp,          Rp,          Yp,          Cq         },
3177         {Qp + 56 * 1, Tp,          Cq,          Wp + 56 * 1},
3178         {Up,          Qp + 56 * 3, Xp + 56 * 1, Dq         },
3179         {Rp,          Up,          Dq,          Zp         },
3180         {Vp,          Rp,          Zp,          Bq         },
3181         {Qp + 56 * 2, Vp,          Bq,          Xp         },
3182  /* layer 7 (the top is the periodic image of the bottom of layer 0) */
3183         {Yp,          Aq,          C + 56 * 4,  A + 56 * 4 },
3184         {Aq,          Wp,          B + 56 * 4,  C + 56 * 4 },
3185         {Cq,          Yp,          A + 56 * 4,  D + 56 * 4 },
3186         {Wp + 56 * 1, Cq,          D + 56 * 4,  B + 56 * 5 },
3187         {Dq,          Xp + 56 * 1, B + 56 * 5,  E + 56 * 4 },
3188         {Zp,          Dq,          E + 56 * 4,  A + 56 * 6 },
3189         {Bq,          Zp,          A + 56 * 6,  F + 56 * 4 },
3190         {Xp,          Bq,          F + 56 * 4,  B + 56 * 4 }
3191       };
3192       const PetscReal gamma                = PetscAcosReal((PetscSqrtReal(3.) - 1.) / PetscSqrtReal(2.)) / PETSC_PI;
3193       const PetscReal patternCoords[56][3] = {
3194         {1.,        0.,        0.  }, /* A  */
3195         {0.,        1.,        0.  }, /* B  */
3196         {gamma,     gamma,     0.  }, /* C  */
3197         {1 + gamma, 1 - gamma, 0.  }, /* D  */
3198         {2 - gamma, 2 - gamma, 0.  }, /* E  */
3199         {1 - gamma, 1 + gamma, 0.  }, /* F  */
3200 
3201         {.5,        0,         .25 }, /* G  */
3202         {1.5,       0.,        .25 }, /* H  */
3203         {.5,        1.,        .25 }, /* II */
3204         {1.5,       1.,        .25 }, /* J  */
3205         {.25,       .5,        .25 }, /* K  */
3206         {1.25,      .5,        .25 }, /* L  */
3207         {.75,       1.5,       .25 }, /* M  */
3208         {1.75,      1.5,       .25 }, /* N  */
3209 
3210         {0.,        0.,        .5  }, /* O  */
3211         {1.,        1.,        .5  }, /* P  */
3212         {gamma,     1 - gamma, .5  }, /* Q  */
3213         {1 + gamma, gamma,     .5  }, /* R  */
3214         {2 - gamma, 1 + gamma, .5  }, /* S  */
3215         {1 - gamma, 2 - gamma, .5  }, /* T  */
3216 
3217         {0.,        .5,        .75 }, /* U  */
3218         {0.,        1.5,       .75 }, /* V  */
3219         {1.,        .5,        .75 }, /* W  */
3220         {1.,        1.5,       .75 }, /* X  */
3221         {.5,        .75,       .75 }, /* Y  */
3222         {.5,        1.75,      .75 }, /* Z  */
3223         {1.5,       .25,       .75 }, /* Ap */
3224         {1.5,       1.25,      .75 }, /* Bp */
3225 
3226         {1.,        0.,        1.  }, /* Cp */
3227         {0.,        1.,        1.  }, /* Dp */
3228         {1 - gamma, 1 - gamma, 1.  }, /* Ep */
3229         {1 + gamma, 1 + gamma, 1.  }, /* Fp */
3230         {2 - gamma, gamma,     1.  }, /* Gp */
3231         {gamma,     2 - gamma, 1.  }, /* Hp */
3232 
3233         {.5,        0.,        1.25}, /* Ip */
3234         {1.5,       0.,        1.25}, /* Jp */
3235         {.5,        1.,        1.25}, /* Kp */
3236         {1.5,       1.,        1.25}, /* Lp */
3237         {.75,       .5,        1.25}, /* Mp */
3238         {1.75,      .5,        1.25}, /* Np */
3239         {.25,       1.5,       1.25}, /* Op */
3240         {1.25,      1.5,       1.25}, /* Pp */
3241 
3242         {0.,        0.,        1.5 }, /* Qp */
3243         {1.,        1.,        1.5 }, /* Rp */
3244         {1 - gamma, gamma,     1.5 }, /* Sp */
3245         {2 - gamma, 1 - gamma, 1.5 }, /* Tp */
3246         {1 + gamma, 2 - gamma, 1.5 }, /* Up */
3247         {gamma,     1 + gamma, 1.5 }, /* Vp */
3248 
3249         {0.,        .5,        1.75}, /* Wp */
3250         {0.,        1.5,       1.75}, /* Xp */
3251         {1.,        .5,        1.75}, /* Yp */
3252         {1.,        1.5,       1.75}, /* Zp */
3253         {.5,        .25,       1.75}, /* Aq */
3254         {.5,        1.25,      1.75}, /* Bq */
3255         {1.5,       .75,       1.75}, /* Cq */
3256         {1.5,       1.75,      1.75}, /* Dq */
3257       };
3258       PetscInt(*cells)[64][4] = NULL;
3259       PetscBool *seen;
3260       PetscInt  *vertToTrueVert;
3261       PetscInt   count;
3262 
3263       for (PetscInt i = 0; i < 3; i++) extentPlus[i] = extent[i] + 1;
3264       numBlocks = 1;
3265       for (PetscInt i = 0; i < 3; i++) numBlocks *= extent[i];
3266       numBlocksPlus = 1;
3267       for (PetscInt i = 0; i < 3; i++) numBlocksPlus *= extentPlus[i];
3268       numFaces = numBlocks * facesPerBlock;
3269       PetscCall(PetscMalloc1(numBlocks, &cells));
3270       PetscCall(PetscCalloc1(numBlocksPlus * vertsPerBlock, &seen));
3271       for (PetscInt k = 0; k < extent[2]; k++) {
3272         for (PetscInt j = 0; j < extent[1]; j++) {
3273           for (PetscInt i = 0; i < extent[0]; i++) {
3274             for (PetscInt f = 0; f < facesPerBlock; f++) {
3275               for (PetscInt v = 0; v < 4; v++) {
3276                 PetscInt vertRaw     = pattern[f][v];
3277                 PetscInt blockidx    = vertRaw / 56;
3278                 PetscInt patternvert = vertRaw % 56;
3279                 PetscInt xplus       = (blockidx & 1);
3280                 PetscInt yplus       = (blockidx & 2) >> 1;
3281                 PetscInt zplus       = (blockidx & 4) >> 2;
3282                 PetscInt zcoord      = (periodic && periodic[2] == DM_BOUNDARY_PERIODIC) ? ((k + zplus) % extent[2]) : (k + zplus);
3283                 PetscInt ycoord      = (periodic && periodic[1] == DM_BOUNDARY_PERIODIC) ? ((j + yplus) % extent[1]) : (j + yplus);
3284                 PetscInt xcoord      = (periodic && periodic[0] == DM_BOUNDARY_PERIODIC) ? ((i + xplus) % extent[0]) : (i + xplus);
3285                 PetscInt vert        = ((zcoord * extentPlus[1] + ycoord) * extentPlus[0] + xcoord) * 56 + patternvert;
3286 
3287                 cells[(k * extent[1] + j) * extent[0] + i][f][v] = vert;
3288                 seen[vert]                                       = PETSC_TRUE;
3289               }
3290             }
3291           }
3292         }
3293       }
3294       for (PetscInt i = 0; i < numBlocksPlus * vertsPerBlock; i++)
3295         if (seen[i]) numVertices++;
3296       count = 0;
3297       PetscCall(PetscMalloc1(numBlocksPlus * vertsPerBlock, &vertToTrueVert));
3298       PetscCall(PetscMalloc1(numVertices * 3, &vtxCoords));
3299       for (PetscInt i = 0; i < numBlocksPlus * vertsPerBlock; i++) vertToTrueVert[i] = -1;
3300       for (PetscInt k = 0; k < extentPlus[2]; k++) {
3301         for (PetscInt j = 0; j < extentPlus[1]; j++) {
3302           for (PetscInt i = 0; i < extentPlus[0]; i++) {
3303             for (PetscInt v = 0; v < vertsPerBlock; v++) {
3304               PetscInt vIdx = ((k * extentPlus[1] + j) * extentPlus[0] + i) * vertsPerBlock + v;
3305 
3306               if (seen[vIdx]) {
3307                 PetscInt thisVert;
3308 
3309                 vertToTrueVert[vIdx] = thisVert = count++;
3310 
3311                 for (PetscInt d = 0; d < 3; d++) vtxCoords[3 * thisVert + d] = patternCoords[v][d];
3312                 vtxCoords[3 * thisVert + 0] += i * 2;
3313                 vtxCoords[3 * thisVert + 1] += j * 2;
3314                 vtxCoords[3 * thisVert + 2] += k * 2;
3315               }
3316             }
3317           }
3318         }
3319       }
3320       for (PetscInt i = 0; i < numBlocks; i++) {
3321         for (PetscInt f = 0; f < facesPerBlock; f++) {
3322           for (PetscInt v = 0; v < 4; v++) cells[i][f][v] = vertToTrueVert[cells[i][f][v]];
3323         }
3324       }
3325       PetscCall(PetscFree(vertToTrueVert));
3326       PetscCall(PetscFree(seen));
3327       cells_flat = cells[0][0];
3328       numEdges   = 0;
3329       for (PetscInt i = 0; i < numFaces; i++) {
3330         for (PetscInt e = 0; e < 4; e++) {
3331           PetscInt         ev[]       = {cells_flat[i * 4 + e], cells_flat[i * 4 + ((e + 1) % 4)]};
3332           const PetscReal *evCoords[] = {&vtxCoords[3 * ev[0]], &vtxCoords[3 * ev[1]]};
3333 
3334           for (PetscInt d = 0; d < 3; d++) {
3335             if (!periodic || periodic[0] != DM_BOUNDARY_PERIODIC) {
3336               if (evCoords[0][d] == 0. && evCoords[1][d] == 0.) numEdges++;
3337               if (evCoords[0][d] == 2. * extent[d] && evCoords[1][d] == 2. * extent[d]) numEdges++;
3338             }
3339           }
3340         }
3341       }
3342       PetscCall(PetscMalloc1(numEdges, &edges));
3343       PetscCall(PetscMalloc1(numEdges, &edgeSets));
3344       for (PetscInt edge = 0, i = 0; i < numFaces; i++) {
3345         for (PetscInt e = 0; e < 4; e++) {
3346           PetscInt         ev[]       = {cells_flat[i * 4 + e], cells_flat[i * 4 + ((e + 1) % 4)]};
3347           const PetscReal *evCoords[] = {&vtxCoords[3 * ev[0]], &vtxCoords[3 * ev[1]]};
3348 
3349           for (PetscInt d = 0; d < 3; d++) {
3350             if (!periodic || periodic[d] != DM_BOUNDARY_PERIODIC) {
3351               if (evCoords[0][d] == 0. && evCoords[1][d] == 0.) {
3352                 edges[edge][0]   = ev[0];
3353                 edges[edge][1]   = ev[1];
3354                 edgeSets[edge++] = 2 * d;
3355               }
3356               if (evCoords[0][d] == 2. * extent[d] && evCoords[1][d] == 2. * extent[d]) {
3357                 edges[edge][0]   = ev[0];
3358                 edges[edge][1]   = ev[1];
3359                 edgeSets[edge++] = 2 * d + 1;
3360               }
3361             }
3362           }
3363         }
3364       }
3365     }
3366     evalFunc   = TPSEvaluate_Gyroid;
3367     normalFunc = TPSExtrudeNormalFunc_Gyroid;
3368     break;
3369   }
3370 
3371   PetscCall(DMSetDimension(dm, topoDim));
3372   if (rank == 0) PetscCall(DMPlexBuildFromCellList(dm, numFaces, numVertices, 4, cells_flat));
3373   else PetscCall(DMPlexBuildFromCellList(dm, 0, 0, 0, NULL));
3374   PetscCall(PetscFree(cells_flat));
3375   {
3376     DM idm;
3377     PetscCall(DMPlexInterpolate(dm, &idm));
3378     PetscCall(DMPlexReplace_Internal(dm, &idm));
3379   }
3380   if (rank == 0) PetscCall(DMPlexBuildCoordinatesFromCellList(dm, spaceDim, vtxCoords));
3381   else PetscCall(DMPlexBuildCoordinatesFromCellList(dm, spaceDim, NULL));
3382   PetscCall(PetscFree(vtxCoords));
3383 
3384   PetscCall(DMCreateLabel(dm, "Face Sets"));
3385   PetscCall(DMGetLabel(dm, "Face Sets", &label));
3386   for (PetscInt e = 0; e < numEdges; e++) {
3387     PetscInt        njoin;
3388     const PetscInt *join, verts[] = {numFaces + edges[e][0], numFaces + edges[e][1]};
3389     PetscCall(DMPlexGetJoin(dm, 2, verts, &njoin, &join));
3390     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]);
3391     PetscCall(DMLabelSetValue(label, join[0], edgeSets[e]));
3392     PetscCall(DMPlexRestoreJoin(dm, 2, verts, &njoin, &join));
3393   }
3394   PetscCall(PetscFree(edges));
3395   PetscCall(PetscFree(edgeSets));
3396   if (tps_distribute) {
3397     DM               pdm = NULL;
3398     PetscPartitioner part;
3399 
3400     PetscCall(DMPlexGetPartitioner(dm, &part));
3401     PetscCall(PetscPartitionerSetFromOptions(part));
3402     PetscCall(DMPlexDistribute(dm, 0, NULL, &pdm));
3403     if (pdm) PetscCall(DMPlexReplace_Internal(dm, &pdm));
3404     // Do not auto-distribute again
3405     PetscCall(DMPlexDistributeSetDefault(dm, PETSC_FALSE));
3406   }
3407 
3408   PetscCall(DMPlexSetRefinementUniform(dm, PETSC_TRUE));
3409   for (PetscInt refine = 0; refine < refinements; refine++) {
3410     PetscInt     m;
3411     DM           dmf;
3412     Vec          X;
3413     PetscScalar *x;
3414     PetscCall(DMRefine(dm, MPI_COMM_NULL, &dmf));
3415     PetscCall(DMPlexReplace_Internal(dm, &dmf));
3416 
3417     PetscCall(DMGetCoordinatesLocal(dm, &X));
3418     PetscCall(VecGetLocalSize(X, &m));
3419     PetscCall(VecGetArray(X, &x));
3420     for (PetscInt i = 0; i < m; i += 3) PetscCall(TPSNearestPoint(evalFunc, &x[i]));
3421     PetscCall(VecRestoreArray(X, &x));
3422   }
3423 
3424   // Face Sets has already been propagated to new vertices during refinement; this propagates to the initial vertices.
3425   PetscCall(DMGetLabel(dm, "Face Sets", &label));
3426   PetscCall(DMPlexLabelComplete(dm, label));
3427 
3428   if (thickness > 0) {
3429     DM              edm, cdm, ecdm;
3430     DMPlexTransform tr;
3431     const char     *prefix;
3432     PetscOptions    options;
3433     // Code from DMPlexExtrude
3434     PetscCall(DMPlexTransformCreate(PetscObjectComm((PetscObject)dm), &tr));
3435     PetscCall(DMPlexTransformSetDM(tr, dm));
3436     PetscCall(DMPlexTransformSetType(tr, DMPLEXEXTRUDE));
3437     PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
3438     PetscCall(PetscObjectSetOptionsPrefix((PetscObject)tr, prefix));
3439     PetscCall(PetscObjectGetOptions((PetscObject)dm, &options));
3440     PetscCall(PetscObjectSetOptions((PetscObject)tr, options));
3441     PetscCall(DMPlexTransformExtrudeSetLayers(tr, layers));
3442     PetscCall(DMPlexTransformExtrudeSetThickness(tr, thickness));
3443     PetscCall(DMPlexTransformExtrudeSetTensor(tr, PETSC_FALSE));
3444     PetscCall(DMPlexTransformExtrudeSetSymmetric(tr, PETSC_TRUE));
3445     PetscCall(DMPlexTransformExtrudeSetNormalFunction(tr, normalFunc));
3446     PetscCall(DMPlexTransformSetFromOptions(tr));
3447     PetscCall(PetscObjectSetOptions((PetscObject)tr, NULL));
3448     PetscCall(DMPlexTransformSetUp(tr));
3449     PetscCall(PetscObjectViewFromOptions((PetscObject)tr, NULL, "-dm_plex_tps_transform_view"));
3450     PetscCall(DMPlexTransformApply(tr, dm, &edm));
3451     PetscCall(DMCopyDisc(dm, edm));
3452     PetscCall(DMGetCoordinateDM(dm, &cdm));
3453     PetscCall(DMGetCoordinateDM(edm, &ecdm));
3454     PetscCall(DMCopyDisc(cdm, ecdm));
3455     PetscCall(DMPlexTransformCreateDiscLabels(tr, edm));
3456     PetscCall(DMPlexTransformDestroy(&tr));
3457     if (edm) {
3458       ((DM_Plex *)edm->data)->printFEM    = ((DM_Plex *)dm->data)->printFEM;
3459       ((DM_Plex *)edm->data)->printL2     = ((DM_Plex *)dm->data)->printL2;
3460       ((DM_Plex *)edm->data)->printLocate = ((DM_Plex *)dm->data)->printLocate;
3461     }
3462     PetscCall(DMPlexReplace_Internal(dm, &edm));
3463   }
3464   PetscFunctionReturn(0);
3465 }
3466 
3467 /*@
3468   DMPlexCreateTPSMesh - Create a distributed, interpolated mesh of a triply-periodic surface
3469 
3470   Collective
3471 
3472   Input Parameters:
3473 + comm   - The communicator for the `DM` object
3474 . tpstype - Type of triply-periodic surface
3475 . extent - Array of length 3 containing number of periods in each direction
3476 . periodic - array of length 3 with periodicity, or NULL for non-periodic
3477 . tps_distribute - Distribute 2D manifold mesh prior to refinement and extrusion (more scalable)
3478 . refinements - Number of factor-of-2 refinements of 2D manifold mesh
3479 . layers - Number of cell layers extruded in normal direction
3480 - thickness - Thickness in normal direction
3481 
3482   Output Parameter:
3483 . dm  - The `DM` object
3484 
3485   Level: beginner
3486 
3487   Notes:
3488   This meshes the surface of the Schwarz P or Gyroid surfaces.  Schwarz P is is the simplest member of the triply-periodic minimal surfaces.
3489   https://en.wikipedia.org/wiki/Schwarz_minimal_surface#Schwarz_P_(%22Primitive%22) and can be cut with "clean" boundaries.
3490   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.
3491   Our implementation creates a very coarse mesh of the surface and refines (by 4-way splitting) as many times as requested.
3492   On each refinement, all vertices are projected to their nearest point on the surface.
3493   This projection could readily be extended to related surfaces.
3494 
3495   The face (edge) sets for the Schwarz P surface are numbered 1(-x), 2(+x), 3(-y), 4(+y), 5(-z), 6(+z).
3496   When the mesh is refined, "Face Sets" contain the new vertices (created during refinement).  Use DMPlexLabelComplete() to propagate to coarse-level vertices.
3497 
3498   Developer Note:
3499   The Gyroid mesh does not currently mark boundary sets.
3500 
3501   References:
3502 . * - Maskery et al, Insights into the mechanical properties of several triply periodic minimal surface lattice structures made by polymer additive manufacturing, 2017.
3503     https://doi.org/10.1016/j.polymer.2017.11.049
3504 
3505 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreateSphereMesh()`, `DMSetType()`, `DMCreate()`
3506 @*/
3507 PetscErrorCode DMPlexCreateTPSMesh(MPI_Comm comm, DMPlexTPSType tpstype, const PetscInt extent[], const DMBoundaryType periodic[], PetscBool tps_distribute, PetscInt refinements, PetscInt layers, PetscReal thickness, DM *dm)
3508 {
3509   PetscFunctionBegin;
3510   PetscCall(DMCreate(comm, dm));
3511   PetscCall(DMSetType(*dm, DMPLEX));
3512   PetscCall(DMPlexCreateTPSMesh_Internal(*dm, tpstype, extent, periodic, tps_distribute, refinements, layers, thickness));
3513   PetscFunctionReturn(0);
3514 }
3515 
3516 /*@
3517   DMPlexCreateSphereMesh - Creates a mesh on the d-dimensional sphere, S^d.
3518 
3519   Collective
3520 
3521   Input Parameters:
3522 + comm    - The communicator for the `DM` object
3523 . dim     - The dimension
3524 . simplex - Use simplices, or tensor product cells
3525 - R       - The radius
3526 
3527   Output Parameter:
3528 . dm  - The `DM` object
3529 
3530   Level: beginner
3531 
3532 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreateBallMesh()`, `DMPlexCreateBoxMesh()`, `DMSetType()`, `DMCreate()`
3533 @*/
3534 PetscErrorCode DMPlexCreateSphereMesh(MPI_Comm comm, PetscInt dim, PetscBool simplex, PetscReal R, DM *dm)
3535 {
3536   PetscFunctionBegin;
3537   PetscValidPointer(dm, 5);
3538   PetscCall(DMCreate(comm, dm));
3539   PetscCall(DMSetType(*dm, DMPLEX));
3540   PetscCall(DMPlexCreateSphereMesh_Internal(*dm, dim, simplex, R));
3541   PetscFunctionReturn(0);
3542 }
3543 
3544 static PetscErrorCode DMPlexCreateBallMesh_Internal(DM dm, PetscInt dim, PetscReal R)
3545 {
3546   DM      sdm, vol;
3547   DMLabel bdlabel;
3548 
3549   PetscFunctionBegin;
3550   PetscCall(DMCreate(PetscObjectComm((PetscObject)dm), &sdm));
3551   PetscCall(DMSetType(sdm, DMPLEX));
3552   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)sdm, "bd_"));
3553   PetscCall(DMPlexCreateSphereMesh_Internal(sdm, dim - 1, PETSC_TRUE, R));
3554   PetscCall(DMSetFromOptions(sdm));
3555   PetscCall(DMViewFromOptions(sdm, NULL, "-dm_view"));
3556   PetscCall(DMPlexGenerate(sdm, NULL, PETSC_TRUE, &vol));
3557   PetscCall(DMDestroy(&sdm));
3558   PetscCall(DMPlexReplace_Internal(dm, &vol));
3559   PetscCall(DMCreateLabel(dm, "marker"));
3560   PetscCall(DMGetLabel(dm, "marker", &bdlabel));
3561   PetscCall(DMPlexMarkBoundaryFaces(dm, PETSC_DETERMINE, bdlabel));
3562   PetscCall(DMPlexLabelComplete(dm, bdlabel));
3563   PetscFunctionReturn(0);
3564 }
3565 
3566 /*@
3567   DMPlexCreateBallMesh - Creates a simplex mesh on the d-dimensional ball, B^d.
3568 
3569   Collective
3570 
3571   Input Parameters:
3572 + comm  - The communicator for the `DM` object
3573 . dim   - The dimension
3574 - R     - The radius
3575 
3576   Output Parameter:
3577 . dm  - The `DM` object
3578 
3579   Options Database Key:
3580 - bd_dm_refine - This will refine the surface mesh preserving the sphere geometry
3581 
3582   Level: beginner
3583 
3584 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreateSphereMesh()`, `DMPlexCreateBoxMesh()`, `DMSetType()`, `DMCreate()`
3585 @*/
3586 PetscErrorCode DMPlexCreateBallMesh(MPI_Comm comm, PetscInt dim, PetscReal R, DM *dm)
3587 {
3588   PetscFunctionBegin;
3589   PetscCall(DMCreate(comm, dm));
3590   PetscCall(DMSetType(*dm, DMPLEX));
3591   PetscCall(DMPlexCreateBallMesh_Internal(*dm, dim, R));
3592   PetscFunctionReturn(0);
3593 }
3594 
3595 static PetscErrorCode DMPlexCreateReferenceCell_Internal(DM rdm, DMPolytopeType ct)
3596 {
3597   PetscFunctionBegin;
3598   switch (ct) {
3599   case DM_POLYTOPE_POINT: {
3600     PetscInt    numPoints[1]        = {1};
3601     PetscInt    coneSize[1]         = {0};
3602     PetscInt    cones[1]            = {0};
3603     PetscInt    coneOrientations[1] = {0};
3604     PetscScalar vertexCoords[1]     = {0.0};
3605 
3606     PetscCall(DMSetDimension(rdm, 0));
3607     PetscCall(DMPlexCreateFromDAG(rdm, 0, numPoints, coneSize, cones, coneOrientations, vertexCoords));
3608   } break;
3609   case DM_POLYTOPE_SEGMENT: {
3610     PetscInt    numPoints[2]        = {2, 1};
3611     PetscInt    coneSize[3]         = {2, 0, 0};
3612     PetscInt    cones[2]            = {1, 2};
3613     PetscInt    coneOrientations[2] = {0, 0};
3614     PetscScalar vertexCoords[2]     = {-1.0, 1.0};
3615 
3616     PetscCall(DMSetDimension(rdm, 1));
3617     PetscCall(DMPlexCreateFromDAG(rdm, 1, numPoints, coneSize, cones, coneOrientations, vertexCoords));
3618   } break;
3619   case DM_POLYTOPE_POINT_PRISM_TENSOR: {
3620     PetscInt    numPoints[2]        = {2, 1};
3621     PetscInt    coneSize[3]         = {2, 0, 0};
3622     PetscInt    cones[2]            = {1, 2};
3623     PetscInt    coneOrientations[2] = {0, 0};
3624     PetscScalar vertexCoords[2]     = {-1.0, 1.0};
3625 
3626     PetscCall(DMSetDimension(rdm, 1));
3627     PetscCall(DMPlexCreateFromDAG(rdm, 1, numPoints, coneSize, cones, coneOrientations, vertexCoords));
3628   } break;
3629   case DM_POLYTOPE_TRIANGLE: {
3630     PetscInt    numPoints[2]        = {3, 1};
3631     PetscInt    coneSize[4]         = {3, 0, 0, 0};
3632     PetscInt    cones[3]            = {1, 2, 3};
3633     PetscInt    coneOrientations[3] = {0, 0, 0};
3634     PetscScalar vertexCoords[6]     = {-1.0, -1.0, 1.0, -1.0, -1.0, 1.0};
3635 
3636     PetscCall(DMSetDimension(rdm, 2));
3637     PetscCall(DMPlexCreateFromDAG(rdm, 1, numPoints, coneSize, cones, coneOrientations, vertexCoords));
3638   } break;
3639   case DM_POLYTOPE_QUADRILATERAL: {
3640     PetscInt    numPoints[2]        = {4, 1};
3641     PetscInt    coneSize[5]         = {4, 0, 0, 0, 0};
3642     PetscInt    cones[4]            = {1, 2, 3, 4};
3643     PetscInt    coneOrientations[4] = {0, 0, 0, 0};
3644     PetscScalar vertexCoords[8]     = {-1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0};
3645 
3646     PetscCall(DMSetDimension(rdm, 2));
3647     PetscCall(DMPlexCreateFromDAG(rdm, 1, numPoints, coneSize, cones, coneOrientations, vertexCoords));
3648   } break;
3649   case DM_POLYTOPE_SEG_PRISM_TENSOR: {
3650     PetscInt    numPoints[2]        = {4, 1};
3651     PetscInt    coneSize[5]         = {4, 0, 0, 0, 0};
3652     PetscInt    cones[4]            = {1, 2, 3, 4};
3653     PetscInt    coneOrientations[4] = {0, 0, 0, 0};
3654     PetscScalar vertexCoords[8]     = {-1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0};
3655 
3656     PetscCall(DMSetDimension(rdm, 2));
3657     PetscCall(DMPlexCreateFromDAG(rdm, 1, numPoints, coneSize, cones, coneOrientations, vertexCoords));
3658   } break;
3659   case DM_POLYTOPE_TETRAHEDRON: {
3660     PetscInt    numPoints[2]        = {4, 1};
3661     PetscInt    coneSize[5]         = {4, 0, 0, 0, 0};
3662     PetscInt    cones[4]            = {1, 2, 3, 4};
3663     PetscInt    coneOrientations[4] = {0, 0, 0, 0};
3664     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};
3665 
3666     PetscCall(DMSetDimension(rdm, 3));
3667     PetscCall(DMPlexCreateFromDAG(rdm, 1, numPoints, coneSize, cones, coneOrientations, vertexCoords));
3668   } break;
3669   case DM_POLYTOPE_HEXAHEDRON: {
3670     PetscInt    numPoints[2]        = {8, 1};
3671     PetscInt    coneSize[9]         = {8, 0, 0, 0, 0, 0, 0, 0, 0};
3672     PetscInt    cones[8]            = {1, 2, 3, 4, 5, 6, 7, 8};
3673     PetscInt    coneOrientations[8] = {0, 0, 0, 0, 0, 0, 0, 0};
3674     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};
3675 
3676     PetscCall(DMSetDimension(rdm, 3));
3677     PetscCall(DMPlexCreateFromDAG(rdm, 1, numPoints, coneSize, cones, coneOrientations, vertexCoords));
3678   } break;
3679   case DM_POLYTOPE_TRI_PRISM: {
3680     PetscInt    numPoints[2]        = {6, 1};
3681     PetscInt    coneSize[7]         = {6, 0, 0, 0, 0, 0, 0};
3682     PetscInt    cones[6]            = {1, 2, 3, 4, 5, 6};
3683     PetscInt    coneOrientations[6] = {0, 0, 0, 0, 0, 0};
3684     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};
3685 
3686     PetscCall(DMSetDimension(rdm, 3));
3687     PetscCall(DMPlexCreateFromDAG(rdm, 1, numPoints, coneSize, cones, coneOrientations, vertexCoords));
3688   } break;
3689   case DM_POLYTOPE_TRI_PRISM_TENSOR: {
3690     PetscInt    numPoints[2]        = {6, 1};
3691     PetscInt    coneSize[7]         = {6, 0, 0, 0, 0, 0, 0};
3692     PetscInt    cones[6]            = {1, 2, 3, 4, 5, 6};
3693     PetscInt    coneOrientations[6] = {0, 0, 0, 0, 0, 0};
3694     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};
3695 
3696     PetscCall(DMSetDimension(rdm, 3));
3697     PetscCall(DMPlexCreateFromDAG(rdm, 1, numPoints, coneSize, cones, coneOrientations, vertexCoords));
3698   } break;
3699   case DM_POLYTOPE_QUAD_PRISM_TENSOR: {
3700     PetscInt    numPoints[2]        = {8, 1};
3701     PetscInt    coneSize[9]         = {8, 0, 0, 0, 0, 0, 0, 0, 0};
3702     PetscInt    cones[8]            = {1, 2, 3, 4, 5, 6, 7, 8};
3703     PetscInt    coneOrientations[8] = {0, 0, 0, 0, 0, 0, 0, 0};
3704     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};
3705 
3706     PetscCall(DMSetDimension(rdm, 3));
3707     PetscCall(DMPlexCreateFromDAG(rdm, 1, numPoints, coneSize, cones, coneOrientations, vertexCoords));
3708   } break;
3709   case DM_POLYTOPE_PYRAMID: {
3710     PetscInt    numPoints[2]        = {5, 1};
3711     PetscInt    coneSize[6]         = {5, 0, 0, 0, 0, 0};
3712     PetscInt    cones[5]            = {1, 2, 3, 4, 5};
3713     PetscInt    coneOrientations[8] = {0, 0, 0, 0, 0, 0, 0, 0};
3714     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};
3715 
3716     PetscCall(DMSetDimension(rdm, 3));
3717     PetscCall(DMPlexCreateFromDAG(rdm, 1, numPoints, coneSize, cones, coneOrientations, vertexCoords));
3718   } break;
3719   default:
3720     SETERRQ(PetscObjectComm((PetscObject)rdm), PETSC_ERR_ARG_WRONG, "Cannot create reference cell for cell type %s", DMPolytopeTypes[ct]);
3721   }
3722   {
3723     PetscInt Nv, v;
3724 
3725     /* Must create the celltype label here so that we do not automatically try to compute the types */
3726     PetscCall(DMCreateLabel(rdm, "celltype"));
3727     PetscCall(DMPlexSetCellType(rdm, 0, ct));
3728     PetscCall(DMPlexGetChart(rdm, NULL, &Nv));
3729     for (v = 1; v < Nv; ++v) PetscCall(DMPlexSetCellType(rdm, v, DM_POLYTOPE_POINT));
3730   }
3731   PetscCall(DMPlexInterpolateInPlace_Internal(rdm));
3732   PetscCall(PetscObjectSetName((PetscObject)rdm, DMPolytopeTypes[ct]));
3733   PetscFunctionReturn(0);
3734 }
3735 
3736 /*@
3737   DMPlexCreateReferenceCell - Create a `DMPLEX` with the appropriate FEM reference cell
3738 
3739   Collective
3740 
3741   Input Parameters:
3742 + comm - The communicator
3743 - ct   - The cell type of the reference cell
3744 
3745   Output Parameter:
3746 . refdm - The reference cell
3747 
3748   Level: intermediate
3749 
3750 .seealso: [](chapter_unstructured), `DM`, `DMPLEX`, `DMPlexCreateReferenceCell()`, `DMPlexCreateBoxMesh()`
3751 @*/
3752 PetscErrorCode DMPlexCreateReferenceCell(MPI_Comm comm, DMPolytopeType ct, DM *refdm)
3753 {
3754   PetscFunctionBegin;
3755   PetscCall(DMCreate(comm, refdm));
3756   PetscCall(DMSetType(*refdm, DMPLEX));
3757   PetscCall(DMPlexCreateReferenceCell_Internal(*refdm, ct));
3758   PetscFunctionReturn(0);
3759 }
3760 
3761 static PetscErrorCode DMPlexCreateBoundaryLabel_Private(DM dm, const char name[])
3762 {
3763   DM        plex;
3764   DMLabel   label;
3765   PetscBool hasLabel;
3766 
3767   PetscFunctionBegin;
3768   PetscCall(DMHasLabel(dm, name, &hasLabel));
3769   if (hasLabel) PetscFunctionReturn(0);
3770   PetscCall(DMCreateLabel(dm, name));
3771   PetscCall(DMGetLabel(dm, name, &label));
3772   PetscCall(DMConvert(dm, DMPLEX, &plex));
3773   PetscCall(DMPlexMarkBoundaryFaces(plex, 1, label));
3774   PetscCall(DMPlexLabelComplete(plex, label));
3775   PetscCall(DMDestroy(&plex));
3776   PetscFunctionReturn(0);
3777 }
3778 
3779 /*
3780   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.
3781 
3782     (x, y) -> (r, theta) = (x[1], (x[0] - lower[0]) * 2\pi/(upper[0] - lower[0]))
3783 */
3784 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[])
3785 {
3786   const PetscReal low = PetscRealPart(constants[0]);
3787   const PetscReal upp = PetscRealPart(constants[1]);
3788   const PetscReal r   = PetscRealPart(u[1]);
3789   const PetscReal th  = 2. * PETSC_PI * (PetscRealPart(u[0]) - low) / (upp - low);
3790 
3791   f0[0] = r * PetscCosReal(th);
3792   f0[1] = r * PetscSinReal(th);
3793 }
3794 
3795 const char *const DMPlexShapes[] = {"box", "box_surface", "ball", "sphere", "cylinder", "schwarz_p", "gyroid", "doublet", "annulus", "hypercubic", "unknown", "DMPlexShape", "DM_SHAPE_", NULL};
3796 
3797 static PetscErrorCode DMPlexCreateFromOptions_Internal(PetscOptionItems *PetscOptionsObject, PetscBool *useCoordSpace, DM dm)
3798 {
3799   DMPlexShape    shape   = DM_SHAPE_BOX;
3800   DMPolytopeType cell    = DM_POLYTOPE_TRIANGLE;
3801   PetscInt       dim     = 2;
3802   PetscBool      simplex = PETSC_TRUE, interpolate = PETSC_TRUE, adjCone = PETSC_FALSE, adjClosure = PETSC_TRUE, refDomain = PETSC_FALSE;
3803   PetscBool      flg, flg2, fflg, bdfflg, nameflg;
3804   MPI_Comm       comm;
3805   char           filename[PETSC_MAX_PATH_LEN]   = "<unspecified>";
3806   char           bdFilename[PETSC_MAX_PATH_LEN] = "<unspecified>";
3807   char           plexname[PETSC_MAX_PATH_LEN]   = "";
3808 
3809   PetscFunctionBegin;
3810   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
3811   /* TODO Turn this into a registration interface */
3812   PetscCall(PetscOptionsString("-dm_plex_filename", "File containing a mesh", "DMPlexCreateFromFile", filename, filename, sizeof(filename), &fflg));
3813   PetscCall(PetscOptionsString("-dm_plex_boundary_filename", "File containing a mesh boundary", "DMPlexCreateFromFile", bdFilename, bdFilename, sizeof(bdFilename), &bdfflg));
3814   PetscCall(PetscOptionsString("-dm_plex_name", "Name of the mesh in the file", "DMPlexCreateFromFile", plexname, plexname, sizeof(plexname), &nameflg));
3815   PetscCall(PetscOptionsEnum("-dm_plex_cell", "Cell shape", "", DMPolytopeTypes, (PetscEnum)cell, (PetscEnum *)&cell, NULL));
3816   PetscCall(PetscOptionsBool("-dm_plex_reference_cell_domain", "Use a reference cell domain", "", refDomain, &refDomain, NULL));
3817   PetscCall(PetscOptionsEnum("-dm_plex_shape", "Shape for built-in mesh", "", DMPlexShapes, (PetscEnum)shape, (PetscEnum *)&shape, &flg));
3818   PetscCall(PetscOptionsBoundedInt("-dm_plex_dim", "Topological dimension of the mesh", "DMGetDimension", dim, &dim, &flg, 0));
3819   PetscCheck(dim >= 0, comm, PETSC_ERR_ARG_OUTOFRANGE, "Dimension %" PetscInt_FMT " should be in [0, infinity)", dim);
3820   PetscCall(PetscOptionsBool("-dm_plex_simplex", "Mesh cell shape", "", simplex, &simplex, &flg));
3821   PetscCall(PetscOptionsBool("-dm_plex_interpolate", "Flag to create edges and faces automatically", "", interpolate, &interpolate, &flg));
3822   PetscCall(PetscOptionsBool("-dm_plex_adj_cone", "Set adjacency direction", "DMSetBasicAdjacency", adjCone, &adjCone, &flg));
3823   PetscCall(PetscOptionsBool("-dm_plex_adj_closure", "Set adjacency size", "DMSetBasicAdjacency", adjClosure, &adjClosure, &flg2));
3824   if (flg || flg2) PetscCall(DMSetBasicAdjacency(dm, adjCone, adjClosure));
3825 
3826   switch (cell) {
3827   case DM_POLYTOPE_POINT:
3828   case DM_POLYTOPE_SEGMENT:
3829   case DM_POLYTOPE_POINT_PRISM_TENSOR:
3830   case DM_POLYTOPE_TRIANGLE:
3831   case DM_POLYTOPE_QUADRILATERAL:
3832   case DM_POLYTOPE_TETRAHEDRON:
3833   case DM_POLYTOPE_HEXAHEDRON:
3834     *useCoordSpace = PETSC_TRUE;
3835     break;
3836   default:
3837     *useCoordSpace = PETSC_FALSE;
3838     break;
3839   }
3840 
3841   if (fflg) {
3842     DM dmnew;
3843 
3844     PetscCall(DMPlexCreateFromFile(PetscObjectComm((PetscObject)dm), filename, plexname, interpolate, &dmnew));
3845     PetscCall(DMPlexCopy_Internal(dm, PETSC_FALSE, PETSC_FALSE, dmnew));
3846     PetscCall(DMPlexReplace_Internal(dm, &dmnew));
3847   } else if (refDomain) {
3848     PetscCall(DMPlexCreateReferenceCell_Internal(dm, cell));
3849   } else if (bdfflg) {
3850     DM bdm, dmnew;
3851 
3852     PetscCall(DMPlexCreateFromFile(PetscObjectComm((PetscObject)dm), bdFilename, plexname, interpolate, &bdm));
3853     PetscCall(PetscObjectSetOptionsPrefix((PetscObject)bdm, "bd_"));
3854     PetscCall(DMSetFromOptions(bdm));
3855     PetscCall(DMPlexGenerate(bdm, NULL, interpolate, &dmnew));
3856     PetscCall(DMDestroy(&bdm));
3857     PetscCall(DMPlexCopy_Internal(dm, PETSC_FALSE, PETSC_FALSE, dmnew));
3858     PetscCall(DMPlexReplace_Internal(dm, &dmnew));
3859   } else {
3860     PetscCall(PetscObjectSetName((PetscObject)dm, DMPlexShapes[shape]));
3861     switch (shape) {
3862     case DM_SHAPE_BOX:
3863     case DM_SHAPE_ANNULUS: {
3864       PetscInt       faces[3]  = {0, 0, 0};
3865       PetscReal      lower[3]  = {0, 0, 0};
3866       PetscReal      upper[3]  = {1, 1, 1};
3867       DMBoundaryType bdt[3]    = {DM_BOUNDARY_NONE, DM_BOUNDARY_NONE, DM_BOUNDARY_NONE};
3868       PetscBool      isAnnular = shape == DM_SHAPE_ANNULUS ? PETSC_TRUE : PETSC_FALSE;
3869       PetscInt       i, n;
3870 
3871       n = dim;
3872       for (i = 0; i < dim; ++i) faces[i] = (dim == 1 ? 1 : 4 - dim);
3873       PetscCall(PetscOptionsIntArray("-dm_plex_box_faces", "Number of faces along each dimension", "", faces, &n, &flg));
3874       n = 3;
3875       PetscCall(PetscOptionsRealArray("-dm_plex_box_lower", "Lower left corner of box", "", lower, &n, &flg));
3876       PetscCheck(!flg || !(n != dim), comm, PETSC_ERR_ARG_SIZ, "Lower box point had %" PetscInt_FMT " values, should have been %" PetscInt_FMT, n, dim);
3877       n = 3;
3878       PetscCall(PetscOptionsRealArray("-dm_plex_box_upper", "Upper right corner of box", "", upper, &n, &flg));
3879       PetscCheck(!flg || !(n != dim), comm, PETSC_ERR_ARG_SIZ, "Upper box point had %" PetscInt_FMT " values, should have been %" PetscInt_FMT, n, dim);
3880       n = 3;
3881       PetscCall(PetscOptionsEnumArray("-dm_plex_box_bd", "Boundary type for each dimension", "", DMBoundaryTypes, (PetscEnum *)bdt, &n, &flg));
3882       PetscCheck(!flg || !(n != dim), comm, PETSC_ERR_ARG_SIZ, "Box boundary types had %" PetscInt_FMT " values, should have been %" PetscInt_FMT, n, dim);
3883 
3884       PetscCheck(!isAnnular || dim == 2, comm, PETSC_ERR_ARG_OUTOFRANGE, "Only two dimensional annuli have been implemented");
3885       if (isAnnular)
3886         for (i = 0; i < dim - 1; ++i) bdt[i] = DM_BOUNDARY_PERIODIC;
3887 
3888       switch (cell) {
3889       case DM_POLYTOPE_TRI_PRISM_TENSOR:
3890         PetscCall(DMPlexCreateWedgeBoxMesh_Internal(dm, faces, lower, upper, bdt));
3891         if (!interpolate) {
3892           DM udm;
3893 
3894           PetscCall(DMPlexUninterpolate(dm, &udm));
3895           PetscCall(DMPlexReplace_Internal(dm, &udm));
3896         }
3897         break;
3898       default:
3899         PetscCall(DMPlexCreateBoxMesh_Internal(dm, dim, simplex, faces, lower, upper, bdt, interpolate));
3900         break;
3901       }
3902       if (isAnnular) {
3903         DM          cdm;
3904         PetscDS     cds;
3905         PetscScalar bounds[2] = {lower[0], upper[0]};
3906 
3907         // Fix coordinates for annular region
3908         PetscCall(DMSetPeriodicity(dm, NULL, NULL, NULL));
3909         PetscCall(DMSetCellCoordinatesLocal(dm, NULL));
3910         PetscCall(DMSetCellCoordinates(dm, NULL));
3911         PetscCall(DMPlexCreateCoordinateSpace(dm, 1, NULL));
3912         PetscCall(DMGetCoordinateDM(dm, &cdm));
3913         PetscCall(DMGetDS(cdm, &cds));
3914         PetscCall(PetscDSSetConstants(cds, 2, bounds));
3915         PetscCall(DMPlexRemapGeometry(dm, 0.0, boxToAnnulus));
3916       }
3917     } break;
3918     case DM_SHAPE_BOX_SURFACE: {
3919       PetscInt  faces[3] = {0, 0, 0};
3920       PetscReal lower[3] = {0, 0, 0};
3921       PetscReal upper[3] = {1, 1, 1};
3922       PetscInt  i, n;
3923 
3924       n = dim + 1;
3925       for (i = 0; i < dim + 1; ++i) faces[i] = (dim + 1 == 1 ? 1 : 4 - (dim + 1));
3926       PetscCall(PetscOptionsIntArray("-dm_plex_box_faces", "Number of faces along each dimension", "", faces, &n, &flg));
3927       n = 3;
3928       PetscCall(PetscOptionsRealArray("-dm_plex_box_lower", "Lower left corner of box", "", lower, &n, &flg));
3929       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);
3930       n = 3;
3931       PetscCall(PetscOptionsRealArray("-dm_plex_box_upper", "Upper right corner of box", "", upper, &n, &flg));
3932       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);
3933       PetscCall(DMPlexCreateBoxSurfaceMesh_Internal(dm, dim + 1, faces, lower, upper, interpolate));
3934     } break;
3935     case DM_SHAPE_SPHERE: {
3936       PetscReal R = 1.0;
3937 
3938       PetscCall(PetscOptionsReal("-dm_plex_sphere_radius", "Radius of the sphere", "", R, &R, &flg));
3939       PetscCall(DMPlexCreateSphereMesh_Internal(dm, dim, simplex, R));
3940     } break;
3941     case DM_SHAPE_BALL: {
3942       PetscReal R = 1.0;
3943 
3944       PetscCall(PetscOptionsReal("-dm_plex_ball_radius", "Radius of the ball", "", R, &R, &flg));
3945       PetscCall(DMPlexCreateBallMesh_Internal(dm, dim, R));
3946     } break;
3947     case DM_SHAPE_CYLINDER: {
3948       DMBoundaryType bdt = DM_BOUNDARY_NONE;
3949       PetscInt       Nw  = 6;
3950 
3951       PetscCall(PetscOptionsEnum("-dm_plex_cylinder_bd", "Boundary type in the z direction", "", DMBoundaryTypes, (PetscEnum)bdt, (PetscEnum *)&bdt, NULL));
3952       PetscCall(PetscOptionsInt("-dm_plex_cylinder_num_wedges", "Number of wedges around the cylinder", "", Nw, &Nw, NULL));
3953       switch (cell) {
3954       case DM_POLYTOPE_TRI_PRISM_TENSOR:
3955         PetscCall(DMPlexCreateWedgeCylinderMesh_Internal(dm, Nw, interpolate));
3956         break;
3957       default:
3958         PetscCall(DMPlexCreateHexCylinderMesh_Internal(dm, bdt));
3959         break;
3960       }
3961     } break;
3962     case DM_SHAPE_SCHWARZ_P: // fallthrough
3963     case DM_SHAPE_GYROID: {
3964       PetscInt       extent[3] = {1, 1, 1}, refine = 0, layers = 0, three;
3965       PetscReal      thickness   = 0.;
3966       DMBoundaryType periodic[3] = {DM_BOUNDARY_NONE, DM_BOUNDARY_NONE, DM_BOUNDARY_NONE};
3967       DMPlexTPSType  tps_type    = shape == DM_SHAPE_SCHWARZ_P ? DMPLEX_TPS_SCHWARZ_P : DMPLEX_TPS_GYROID;
3968       PetscBool      tps_distribute;
3969       PetscCall(PetscOptionsIntArray("-dm_plex_tps_extent", "Number of replicas for each of three dimensions", NULL, extent, (three = 3, &three), NULL));
3970       PetscCall(PetscOptionsInt("-dm_plex_tps_refine", "Number of refinements", NULL, refine, &refine, NULL));
3971       PetscCall(PetscOptionsEnumArray("-dm_plex_tps_periodic", "Periodicity in each of three dimensions", NULL, DMBoundaryTypes, (PetscEnum *)periodic, (three = 3, &three), NULL));
3972       PetscCall(PetscOptionsInt("-dm_plex_tps_layers", "Number of layers in volumetric extrusion (or zero to not extrude)", NULL, layers, &layers, NULL));
3973       PetscCall(PetscOptionsReal("-dm_plex_tps_thickness", "Thickness of volumetric extrusion", NULL, thickness, &thickness, NULL));
3974       PetscCall(DMPlexDistributeGetDefault(dm, &tps_distribute));
3975       PetscCall(PetscOptionsBool("-dm_plex_tps_distribute", "Distribute the 2D mesh prior to refinement and extrusion", NULL, tps_distribute, &tps_distribute, NULL));
3976       PetscCall(DMPlexCreateTPSMesh_Internal(dm, tps_type, extent, periodic, tps_distribute, refine, layers, thickness));
3977     } break;
3978     case DM_SHAPE_DOUBLET: {
3979       DM        dmnew;
3980       PetscReal rl = 0.0;
3981 
3982       PetscCall(PetscOptionsReal("-dm_plex_doublet_refinementlimit", "Refinement limit", NULL, rl, &rl, NULL));
3983       PetscCall(DMPlexCreateDoublet(PetscObjectComm((PetscObject)dm), dim, simplex, interpolate, rl, &dmnew));
3984       PetscCall(DMPlexCopy_Internal(dm, PETSC_FALSE, PETSC_FALSE, dmnew));
3985       PetscCall(DMPlexReplace_Internal(dm, &dmnew));
3986     } break;
3987     case DM_SHAPE_HYPERCUBIC: {
3988       PetscInt       *edges;
3989       PetscReal      *lower, *upper;
3990       DMBoundaryType *bdt;
3991       PetscInt        n, d;
3992 
3993       *useCoordSpace = PETSC_FALSE;
3994       PetscCall(PetscMalloc4(dim, &edges, dim, &lower, dim, &upper, dim, &bdt));
3995       for (d = 0; d < dim; ++d) {
3996         edges[d] = 1;
3997         lower[d] = 0.;
3998         upper[d] = 1.;
3999         bdt[d]   = DM_BOUNDARY_PERIODIC;
4000       }
4001       n = dim;
4002       PetscCall(PetscOptionsIntArray("-dm_plex_box_faces", "Number of faces along each dimension", "", edges, &n, &flg));
4003       n = dim;
4004       PetscCall(PetscOptionsRealArray("-dm_plex_box_lower", "Lower left corner of box", "", lower, &n, &flg));
4005       PetscCheck(!flg || n == dim, comm, PETSC_ERR_ARG_SIZ, "Lower box point had %" PetscInt_FMT " values, should have been %" PetscInt_FMT, n, dim);
4006       n = dim;
4007       PetscCall(PetscOptionsRealArray("-dm_plex_box_upper", "Upper right corner of box", "", upper, &n, &flg));
4008       PetscCheck(!flg || n == dim, comm, PETSC_ERR_ARG_SIZ, "Upper box point had %" PetscInt_FMT " values, should have been %" PetscInt_FMT, n, dim);
4009       n = dim;
4010       PetscCall(PetscOptionsEnumArray("-dm_plex_box_bd", "Boundary type for each dimension", "", DMBoundaryTypes, (PetscEnum *)bdt, &n, &flg));
4011       PetscCheck(!flg || n == dim, comm, PETSC_ERR_ARG_SIZ, "Box boundary types had %" PetscInt_FMT " values, should have been %" PetscInt_FMT, n, dim);
4012       PetscCall(DMPlexCreateHypercubicMesh_Internal(dm, dim, lower, upper, edges, bdt));
4013       PetscCall(PetscFree4(edges, lower, upper, bdt));
4014     } break;
4015     default:
4016       SETERRQ(comm, PETSC_ERR_SUP, "Domain shape %s is unsupported", DMPlexShapes[shape]);
4017     }
4018   }
4019   PetscCall(DMPlexSetRefinementUniform(dm, PETSC_TRUE));
4020   if (!((PetscObject)dm)->name && nameflg) PetscCall(PetscObjectSetName((PetscObject)dm, plexname));
4021   PetscFunctionReturn(0);
4022 }
4023 
4024 PetscErrorCode DMSetFromOptions_NonRefinement_Plex(DM dm, PetscOptionItems *PetscOptionsObject)
4025 {
4026   DM_Plex  *mesh = (DM_Plex *)dm->data;
4027   PetscBool flg, flg2;
4028   char      bdLabel[PETSC_MAX_PATH_LEN];
4029 
4030   PetscFunctionBegin;
4031   /* Handle viewing */
4032   PetscCall(PetscOptionsBool("-dm_plex_print_set_values", "Output all set values info", "DMPlexMatSetClosure", PETSC_FALSE, &mesh->printSetValues, NULL));
4033   PetscCall(PetscOptionsBoundedInt("-dm_plex_print_fem", "Debug output level all fem computations", "DMPlexSNESComputeResidualFEM", 0, &mesh->printFEM, NULL, 0));
4034   PetscCall(PetscOptionsReal("-dm_plex_print_tol", "Tolerance for FEM output", "DMPlexSNESComputeResidualFEM", mesh->printTol, &mesh->printTol, NULL));
4035   PetscCall(PetscOptionsBoundedInt("-dm_plex_print_l2", "Debug output level all L2 diff computations", "DMComputeL2Diff", 0, &mesh->printL2, NULL, 0));
4036   PetscCall(PetscOptionsBoundedInt("-dm_plex_print_locate", "Debug output level all point location computations", "DMLocatePoints", 0, &mesh->printLocate, NULL, 0));
4037   PetscCall(DMMonitorSetFromOptions(dm, "-dm_plex_monitor_throughput", "Monitor the simulation throughput", "DMPlexMonitorThroughput", DMPlexMonitorThroughput, NULL, &flg));
4038   if (flg) PetscCall(PetscLogDefaultBegin());
4039   /* Labeling */
4040   PetscCall(PetscOptionsString("-dm_plex_boundary_label", "Label to mark the mesh boundary", "", bdLabel, bdLabel, sizeof(bdLabel), &flg));
4041   if (flg) PetscCall(DMPlexCreateBoundaryLabel_Private(dm, bdLabel));
4042   /* Point Location */
4043   PetscCall(PetscOptionsBool("-dm_plex_hash_location", "Use grid hashing for point location", "DMInterpolate", PETSC_FALSE, &mesh->useHashLocation, NULL));
4044   /* Partitioning and distribution */
4045   PetscCall(PetscOptionsBool("-dm_plex_partition_balance", "Attempt to evenly divide points on partition boundary between processes", "DMPlexSetPartitionBalance", PETSC_FALSE, &mesh->partitionBalance, NULL));
4046   /* Generation and remeshing */
4047   PetscCall(PetscOptionsBool("-dm_plex_remesh_bd", "Allow changes to the boundary on remeshing", "DMAdapt", PETSC_FALSE, &mesh->remeshBd, NULL));
4048   /* Projection behavior */
4049   PetscCall(PetscOptionsBoundedInt("-dm_plex_max_projection_height", "Maximum mesh point height used to project locally", "DMPlexSetMaxProjectionHeight", 0, &mesh->maxProjectionHeight, NULL, 0));
4050   PetscCall(PetscOptionsBool("-dm_plex_regular_refinement", "Use special nested projection algorithm for regular refinement", "DMPlexSetRegularRefinement", mesh->regularRefinement, &mesh->regularRefinement, NULL));
4051   /* Checking structure */
4052   {
4053     PetscBool all = PETSC_FALSE;
4054 
4055     PetscCall(PetscOptionsBool("-dm_plex_check_all", "Perform all basic checks", "DMPlexCheck", PETSC_FALSE, &all, NULL));
4056     if (all) {
4057       PetscCall(DMPlexCheck(dm));
4058     } else {
4059       PetscCall(PetscOptionsBool("-dm_plex_check_symmetry", "Check that the adjacency information in the mesh is symmetric", "DMPlexCheckSymmetry", PETSC_FALSE, &flg, &flg2));
4060       if (flg && flg2) PetscCall(DMPlexCheckSymmetry(dm));
4061       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));
4062       if (flg && flg2) PetscCall(DMPlexCheckSkeleton(dm, 0));
4063       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));
4064       if (flg && flg2) PetscCall(DMPlexCheckFaces(dm, 0));
4065       PetscCall(PetscOptionsBool("-dm_plex_check_geometry", "Check that cells have positive volume", "DMPlexCheckGeometry", PETSC_FALSE, &flg, &flg2));
4066       if (flg && flg2) PetscCall(DMPlexCheckGeometry(dm));
4067       PetscCall(PetscOptionsBool("-dm_plex_check_pointsf", "Check some necessary conditions for PointSF", "DMPlexCheckPointSF", PETSC_FALSE, &flg, &flg2));
4068       if (flg && flg2) PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
4069       PetscCall(PetscOptionsBool("-dm_plex_check_interface_cones", "Check points on inter-partition interfaces have conforming order of cone points", "DMPlexCheckInterfaceCones", PETSC_FALSE, &flg, &flg2));
4070       if (flg && flg2) PetscCall(DMPlexCheckInterfaceCones(dm));
4071     }
4072     PetscCall(PetscOptionsBool("-dm_plex_check_cell_shape", "Check cell shape", "DMPlexCheckCellShape", PETSC_FALSE, &flg, &flg2));
4073     if (flg && flg2) PetscCall(DMPlexCheckCellShape(dm, PETSC_TRUE, PETSC_DETERMINE));
4074   }
4075   {
4076     PetscReal scale = 1.0;
4077 
4078     PetscCall(PetscOptionsReal("-dm_plex_scale", "Scale factor for mesh coordinates", "DMPlexScale", scale, &scale, &flg));
4079     if (flg) {
4080       Vec coordinates, coordinatesLocal;
4081 
4082       PetscCall(DMGetCoordinates(dm, &coordinates));
4083       PetscCall(DMGetCoordinatesLocal(dm, &coordinatesLocal));
4084       PetscCall(VecScale(coordinates, scale));
4085       PetscCall(VecScale(coordinatesLocal, scale));
4086     }
4087   }
4088   PetscCall(PetscPartitionerSetFromOptions(mesh->partitioner));
4089   PetscFunctionReturn(0);
4090 }
4091 
4092 PetscErrorCode DMSetFromOptions_Overlap_Plex(DM dm, PetscOptionItems *PetscOptionsObject, PetscInt *overlap)
4093 {
4094   PetscInt  numOvLabels = 16, numOvExLabels = 16;
4095   char     *ovLabelNames[16], *ovExLabelNames[16];
4096   PetscInt  numOvValues = 16, numOvExValues = 16, l;
4097   PetscBool flg;
4098 
4099   PetscFunctionBegin;
4100   PetscCall(PetscOptionsBoundedInt("-dm_distribute_overlap", "The size of the overlap halo", "DMPlexDistribute", *overlap, overlap, NULL, 0));
4101   PetscCall(PetscOptionsStringArray("-dm_distribute_overlap_labels", "List of overlap label names", "DMPlexDistribute", ovLabelNames, &numOvLabels, &flg));
4102   if (!flg) numOvLabels = 0;
4103   if (numOvLabels) {
4104     ((DM_Plex *)dm->data)->numOvLabels = numOvLabels;
4105     for (l = 0; l < numOvLabels; ++l) {
4106       PetscCall(DMGetLabel(dm, ovLabelNames[l], &((DM_Plex *)dm->data)->ovLabels[l]));
4107       PetscCheck(((DM_Plex *)dm->data)->ovLabels[l], PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Invalid label name %s", ovLabelNames[l]);
4108       PetscCall(PetscFree(ovLabelNames[l]));
4109     }
4110     PetscCall(PetscOptionsIntArray("-dm_distribute_overlap_values", "List of overlap label values", "DMPlexDistribute", ((DM_Plex *)dm->data)->ovValues, &numOvValues, &flg));
4111     if (!flg) numOvValues = 0;
4112     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);
4113 
4114     PetscCall(PetscOptionsStringArray("-dm_distribute_overlap_exclude_labels", "List of overlap exclude label names", "DMPlexDistribute", ovExLabelNames, &numOvExLabels, &flg));
4115     if (!flg) numOvExLabels = 0;
4116     ((DM_Plex *)dm->data)->numOvExLabels = numOvExLabels;
4117     for (l = 0; l < numOvExLabels; ++l) {
4118       PetscCall(DMGetLabel(dm, ovExLabelNames[l], &((DM_Plex *)dm->data)->ovExLabels[l]));
4119       PetscCheck(((DM_Plex *)dm->data)->ovExLabels[l], PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Invalid label name %s", ovExLabelNames[l]);
4120       PetscCall(PetscFree(ovExLabelNames[l]));
4121     }
4122     PetscCall(PetscOptionsIntArray("-dm_distribute_overlap_exclude_values", "List of overlap exclude label values", "DMPlexDistribute", ((DM_Plex *)dm->data)->ovExValues, &numOvExValues, &flg));
4123     if (!flg) numOvExValues = 0;
4124     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);
4125   }
4126   PetscFunctionReturn(0);
4127 }
4128 
4129 static PetscErrorCode DMSetFromOptions_Plex(DM dm, PetscOptionItems *PetscOptionsObject)
4130 {
4131   PetscFunctionList        ordlist;
4132   char                     oname[256];
4133   PetscReal                volume    = -1.0;
4134   PetscInt                 prerefine = 0, refine = 0, r, coarsen = 0, overlap = 0, extLayers = 0, dim;
4135   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;
4136   DMPlexReorderDefaultFlag reorder;
4137 
4138   PetscFunctionBegin;
4139   PetscOptionsHeadBegin(PetscOptionsObject, "DMPlex Options");
4140   if (dm->cloneOpts) goto non_refine;
4141   /* Handle automatic creation */
4142   PetscCall(DMGetDimension(dm, &dim));
4143   if (dim < 0) {
4144     PetscCall(DMPlexCreateFromOptions_Internal(PetscOptionsObject, &coordSpace, dm));
4145     created = PETSC_TRUE;
4146   }
4147   PetscCall(DMGetDimension(dm, &dim));
4148   /* Handle interpolation before distribution */
4149   PetscCall(PetscOptionsBool("-dm_plex_interpolate_pre", "Flag to interpolate mesh before distribution", "", interpolate, &interpolate, &flg));
4150   if (flg) {
4151     DMPlexInterpolatedFlag interpolated;
4152 
4153     PetscCall(DMPlexIsInterpolated(dm, &interpolated));
4154     if (interpolated == DMPLEX_INTERPOLATED_FULL && !interpolate) {
4155       DM udm;
4156 
4157       PetscCall(DMPlexUninterpolate(dm, &udm));
4158       PetscCall(DMPlexReplace_Internal(dm, &udm));
4159     } else if (interpolated != DMPLEX_INTERPOLATED_FULL && interpolate) {
4160       DM idm;
4161 
4162       PetscCall(DMPlexInterpolate(dm, &idm));
4163       PetscCall(DMPlexReplace_Internal(dm, &idm));
4164     }
4165   }
4166   /* Handle DMPlex refinement before distribution */
4167   PetscCall(PetscOptionsBool("-dm_refine_ignore_model", "Flag to ignore the geometry model when refining", "DMCreate", ignoreModel, &ignoreModel, &flg));
4168   if (flg) ((DM_Plex *)dm->data)->ignoreModel = ignoreModel;
4169   PetscCall(DMPlexGetRefinementUniform(dm, &uniformOrig));
4170   PetscCall(PetscOptionsBoundedInt("-dm_refine_pre", "The number of refinements before distribution", "DMCreate", prerefine, &prerefine, NULL, 0));
4171   PetscCall(PetscOptionsBool("-dm_refine_remap_pre", "Flag to control coordinate remapping", "DMCreate", remap, &remap, NULL));
4172   PetscCall(PetscOptionsBool("-dm_refine_uniform_pre", "Flag for uniform refinement before distribution", "DMCreate", uniform, &uniform, &flg));
4173   if (flg) PetscCall(DMPlexSetRefinementUniform(dm, uniform));
4174   PetscCall(PetscOptionsReal("-dm_refine_volume_limit_pre", "The maximum cell volume after refinement before distribution", "DMCreate", volume, &volume, &flg));
4175   if (flg) {
4176     PetscCall(DMPlexSetRefinementUniform(dm, PETSC_FALSE));
4177     PetscCall(DMPlexSetRefinementLimit(dm, volume));
4178     prerefine = PetscMax(prerefine, 1);
4179   }
4180   for (r = 0; r < prerefine; ++r) {
4181     DM             rdm;
4182     PetscPointFunc coordFunc = ((DM_Plex *)dm->data)->coordFunc;
4183 
4184     PetscCall(DMSetFromOptions_NonRefinement_Plex(dm, PetscOptionsObject));
4185     PetscCall(DMRefine(dm, PetscObjectComm((PetscObject)dm), &rdm));
4186     PetscCall(DMPlexReplace_Internal(dm, &rdm));
4187     PetscCall(DMSetFromOptions_NonRefinement_Plex(dm, PetscOptionsObject));
4188     if (coordFunc && remap) {
4189       PetscCall(DMPlexRemapGeometry(dm, 0.0, coordFunc));
4190       ((DM_Plex *)dm->data)->coordFunc = coordFunc;
4191     }
4192   }
4193   PetscCall(DMPlexSetRefinementUniform(dm, uniformOrig));
4194   /* Handle DMPlex extrusion before distribution */
4195   PetscCall(PetscOptionsBoundedInt("-dm_extrude", "The number of layers to extrude", "", extLayers, &extLayers, NULL, 0));
4196   if (extLayers) {
4197     DM edm;
4198 
4199     PetscCall(DMExtrude(dm, extLayers, &edm));
4200     PetscCall(DMPlexReplace_Internal(dm, &edm));
4201     ((DM_Plex *)dm->data)->coordFunc = NULL;
4202     PetscCall(DMSetFromOptions_NonRefinement_Plex(dm, PetscOptionsObject));
4203     extLayers = 0;
4204     PetscCall(DMGetDimension(dm, &dim));
4205   }
4206   /* Handle DMPlex reordering before distribution */
4207   PetscCall(DMPlexReorderGetDefault(dm, &reorder));
4208   PetscCall(MatGetOrderingList(&ordlist));
4209   PetscCall(PetscStrncpy(oname, MATORDERINGNATURAL, sizeof(oname)));
4210   PetscCall(PetscOptionsFList("-dm_plex_reorder", "Set mesh reordering type", "DMPlexGetOrdering", ordlist, MATORDERINGNATURAL, oname, sizeof(oname), &flg));
4211   if (reorder == DMPLEX_REORDER_DEFAULT_TRUE || flg) {
4212     DM pdm;
4213     IS perm;
4214 
4215     PetscCall(DMPlexGetOrdering(dm, oname, NULL, &perm));
4216     PetscCall(DMPlexPermute(dm, perm, &pdm));
4217     PetscCall(ISDestroy(&perm));
4218     PetscCall(DMPlexReplace_Internal(dm, &pdm));
4219     PetscCall(DMSetFromOptions_NonRefinement_Plex(dm, PetscOptionsObject));
4220   }
4221   /* Handle DMPlex distribution */
4222   PetscCall(DMPlexDistributeGetDefault(dm, &distribute));
4223   PetscCall(PetscOptionsBool("-dm_distribute", "Flag to redistribute a mesh among processes", "DMPlexDistribute", distribute, &distribute, NULL));
4224   PetscCall(DMSetFromOptions_Overlap_Plex(dm, PetscOptionsObject, &overlap));
4225   if (distribute) {
4226     DM               pdm = NULL;
4227     PetscPartitioner part;
4228 
4229     PetscCall(DMPlexGetPartitioner(dm, &part));
4230     PetscCall(PetscPartitionerSetFromOptions(part));
4231     PetscCall(DMPlexDistribute(dm, overlap, NULL, &pdm));
4232     if (pdm) PetscCall(DMPlexReplace_Internal(dm, &pdm));
4233   }
4234   /* Create coordinate space */
4235   if (created) {
4236     DM_Plex  *mesh   = (DM_Plex *)dm->data;
4237     PetscInt  degree = 1;
4238     PetscInt  height = 0;
4239     DM        cdm;
4240     PetscBool flg;
4241 
4242     PetscCall(PetscOptionsBool("-dm_coord_space", "Use an FEM space for coordinates", "", coordSpace, &coordSpace, &flg));
4243     PetscCall(PetscOptionsInt("-dm_coord_petscspace_degree", "FEM degree for coordinate space", "", degree, &degree, NULL));
4244     if (coordSpace) PetscCall(DMPlexCreateCoordinateSpace(dm, degree, mesh->coordFunc));
4245     PetscCall(DMGetCoordinateDM(dm, &cdm));
4246     if (flg && !coordSpace) {
4247       PetscDS      cds;
4248       PetscObject  obj;
4249       PetscClassId id;
4250 
4251       PetscCall(DMGetDS(cdm, &cds));
4252       PetscCall(PetscDSGetDiscretization(cds, 0, &obj));
4253       PetscCall(PetscObjectGetClassId(obj, &id));
4254       if (id == PETSCFE_CLASSID) {
4255         PetscContainer dummy;
4256 
4257         PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &dummy));
4258         PetscCall(PetscObjectSetName((PetscObject)dummy, "coordinates"));
4259         PetscCall(DMSetField(cdm, 0, NULL, (PetscObject)dummy));
4260         PetscCall(PetscContainerDestroy(&dummy));
4261         PetscCall(DMClearDS(cdm));
4262       }
4263       mesh->coordFunc = NULL;
4264     }
4265     PetscCall(PetscOptionsBool("-dm_sparse_localize", "Localize only necessary cells", "", dm->sparseLocalize, &dm->sparseLocalize, &flg));
4266     PetscCall(PetscOptionsInt("-dm_localize_height", "Localize edges and faces in addition to cells", "", height, &height, &flg));
4267     if (flg) PetscCall(DMPlexSetMaxProjectionHeight(cdm, height));
4268     PetscCall(DMLocalizeCoordinates(dm));
4269   }
4270   /* Handle DMPlex refinement */
4271   remap = PETSC_TRUE;
4272   PetscCall(PetscOptionsBoundedInt("-dm_refine", "The number of uniform refinements", "DMCreate", refine, &refine, NULL, 0));
4273   PetscCall(PetscOptionsBool("-dm_refine_remap", "Flag to control coordinate remapping", "DMCreate", remap, &remap, NULL));
4274   PetscCall(PetscOptionsBoundedInt("-dm_refine_hierarchy", "The number of uniform refinements", "DMCreate", refine, &refine, &isHierarchy, 0));
4275   if (refine) PetscCall(DMPlexSetRefinementUniform(dm, PETSC_TRUE));
4276   if (refine && isHierarchy) {
4277     DM *dms, coarseDM;
4278 
4279     PetscCall(DMGetCoarseDM(dm, &coarseDM));
4280     PetscCall(PetscObjectReference((PetscObject)coarseDM));
4281     PetscCall(PetscMalloc1(refine, &dms));
4282     PetscCall(DMRefineHierarchy(dm, refine, dms));
4283     /* Total hack since we do not pass in a pointer */
4284     PetscCall(DMPlexSwap_Static(dm, dms[refine - 1]));
4285     if (refine == 1) {
4286       PetscCall(DMSetCoarseDM(dm, dms[0]));
4287       PetscCall(DMPlexSetRegularRefinement(dm, PETSC_TRUE));
4288     } else {
4289       PetscCall(DMSetCoarseDM(dm, dms[refine - 2]));
4290       PetscCall(DMPlexSetRegularRefinement(dm, PETSC_TRUE));
4291       PetscCall(DMSetCoarseDM(dms[0], dms[refine - 1]));
4292       PetscCall(DMPlexSetRegularRefinement(dms[0], PETSC_TRUE));
4293     }
4294     PetscCall(DMSetCoarseDM(dms[refine - 1], coarseDM));
4295     PetscCall(PetscObjectDereference((PetscObject)coarseDM));
4296     /* Free DMs */
4297     for (r = 0; r < refine; ++r) {
4298       PetscCall(DMSetFromOptions_NonRefinement_Plex(dms[r], PetscOptionsObject));
4299       PetscCall(DMDestroy(&dms[r]));
4300     }
4301     PetscCall(PetscFree(dms));
4302   } else {
4303     for (r = 0; r < refine; ++r) {
4304       DM             rdm;
4305       PetscPointFunc coordFunc = ((DM_Plex *)dm->data)->coordFunc;
4306 
4307       PetscCall(DMSetFromOptions_NonRefinement_Plex(dm, PetscOptionsObject));
4308       PetscCall(DMRefine(dm, PetscObjectComm((PetscObject)dm), &rdm));
4309       /* Total hack since we do not pass in a pointer */
4310       PetscCall(DMPlexReplace_Internal(dm, &rdm));
4311       PetscCall(DMSetFromOptions_NonRefinement_Plex(dm, PetscOptionsObject));
4312       if (coordFunc && remap) {
4313         PetscCall(DMPlexRemapGeometry(dm, 0.0, coordFunc));
4314         ((DM_Plex *)dm->data)->coordFunc = coordFunc;
4315       }
4316     }
4317   }
4318   /* Handle DMPlex coarsening */
4319   PetscCall(PetscOptionsBoundedInt("-dm_coarsen", "Coarsen the mesh", "DMCreate", coarsen, &coarsen, NULL, 0));
4320   PetscCall(PetscOptionsBoundedInt("-dm_coarsen_hierarchy", "The number of coarsenings", "DMCreate", coarsen, &coarsen, &isHierarchy, 0));
4321   if (coarsen && isHierarchy) {
4322     DM *dms;
4323 
4324     PetscCall(PetscMalloc1(coarsen, &dms));
4325     PetscCall(DMCoarsenHierarchy(dm, coarsen, dms));
4326     /* Free DMs */
4327     for (r = 0; r < coarsen; ++r) {
4328       PetscCall(DMSetFromOptions_NonRefinement_Plex(dms[r], PetscOptionsObject));
4329       PetscCall(DMDestroy(&dms[r]));
4330     }
4331     PetscCall(PetscFree(dms));
4332   } else {
4333     for (r = 0; r < coarsen; ++r) {
4334       DM             cdm;
4335       PetscPointFunc coordFunc = ((DM_Plex *)dm->data)->coordFunc;
4336 
4337       PetscCall(DMSetFromOptions_NonRefinement_Plex(dm, PetscOptionsObject));
4338       PetscCall(DMCoarsen(dm, PetscObjectComm((PetscObject)dm), &cdm));
4339       /* Total hack since we do not pass in a pointer */
4340       PetscCall(DMPlexReplace_Internal(dm, &cdm));
4341       PetscCall(DMSetFromOptions_NonRefinement_Plex(dm, PetscOptionsObject));
4342       if (coordFunc) {
4343         PetscCall(DMPlexRemapGeometry(dm, 0.0, coordFunc));
4344         ((DM_Plex *)dm->data)->coordFunc = coordFunc;
4345       }
4346     }
4347   }
4348   /* Handle ghost cells */
4349   PetscCall(PetscOptionsBool("-dm_plex_create_fv_ghost_cells", "Flag to create finite volume ghost cells on the boundary", "DMCreate", ghostCells, &ghostCells, NULL));
4350   if (ghostCells) {
4351     DM   gdm;
4352     char lname[PETSC_MAX_PATH_LEN];
4353 
4354     lname[0] = '\0';
4355     PetscCall(PetscOptionsString("-dm_plex_fv_ghost_cells_label", "Label name for ghost cells boundary", "DMCreate", lname, lname, sizeof(lname), &flg));
4356     PetscCall(DMPlexConstructGhostCells(dm, flg ? lname : NULL, NULL, &gdm));
4357     PetscCall(DMPlexReplace_Internal(dm, &gdm));
4358   }
4359   /* Handle 1D order */
4360   if (reorder != DMPLEX_REORDER_DEFAULT_FALSE && dim == 1) {
4361     DM           cdm, rdm;
4362     PetscDS      cds;
4363     PetscObject  obj;
4364     PetscClassId id = PETSC_OBJECT_CLASSID;
4365     IS           perm;
4366     PetscInt     Nf;
4367     PetscBool    distributed;
4368 
4369     PetscCall(DMPlexIsDistributed(dm, &distributed));
4370     PetscCall(DMGetCoordinateDM(dm, &cdm));
4371     PetscCall(DMGetDS(cdm, &cds));
4372     PetscCall(PetscDSGetNumFields(cds, &Nf));
4373     if (Nf) {
4374       PetscCall(PetscDSGetDiscretization(cds, 0, &obj));
4375       PetscCall(PetscObjectGetClassId(obj, &id));
4376     }
4377     if (!distributed && id != PETSCFE_CLASSID) {
4378       PetscCall(DMPlexGetOrdering1D(dm, &perm));
4379       PetscCall(DMPlexPermute(dm, perm, &rdm));
4380       PetscCall(DMPlexReplace_Internal(dm, &rdm));
4381       PetscCall(ISDestroy(&perm));
4382     }
4383   }
4384 /* Handle */
4385 non_refine:
4386   PetscCall(DMSetFromOptions_NonRefinement_Plex(dm, PetscOptionsObject));
4387   PetscOptionsHeadEnd();
4388   PetscFunctionReturn(0);
4389 }
4390 
4391 static PetscErrorCode DMCreateGlobalVector_Plex(DM dm, Vec *vec)
4392 {
4393   PetscFunctionBegin;
4394   PetscCall(DMCreateGlobalVector_Section_Private(dm, vec));
4395   /* PetscCall(VecSetOperation(*vec, VECOP_DUPLICATE, (void(*)(void)) VecDuplicate_MPI_DM)); */
4396   PetscCall(VecSetOperation(*vec, VECOP_VIEW, (void (*)(void))VecView_Plex));
4397   PetscCall(VecSetOperation(*vec, VECOP_VIEWNATIVE, (void (*)(void))VecView_Plex_Native));
4398   PetscCall(VecSetOperation(*vec, VECOP_LOAD, (void (*)(void))VecLoad_Plex));
4399   PetscCall(VecSetOperation(*vec, VECOP_LOADNATIVE, (void (*)(void))VecLoad_Plex_Native));
4400   PetscFunctionReturn(0);
4401 }
4402 
4403 static PetscErrorCode DMCreateLocalVector_Plex(DM dm, Vec *vec)
4404 {
4405   PetscFunctionBegin;
4406   PetscCall(DMCreateLocalVector_Section_Private(dm, vec));
4407   PetscCall(VecSetOperation(*vec, VECOP_VIEW, (void (*)(void))VecView_Plex_Local));
4408   PetscCall(VecSetOperation(*vec, VECOP_LOAD, (void (*)(void))VecLoad_Plex_Local));
4409   PetscFunctionReturn(0);
4410 }
4411 
4412 static PetscErrorCode DMGetDimPoints_Plex(DM dm, PetscInt dim, PetscInt *pStart, PetscInt *pEnd)
4413 {
4414   PetscInt depth, d;
4415 
4416   PetscFunctionBegin;
4417   PetscCall(DMPlexGetDepth(dm, &depth));
4418   if (depth == 1) {
4419     PetscCall(DMGetDimension(dm, &d));
4420     if (dim == 0) PetscCall(DMPlexGetDepthStratum(dm, dim, pStart, pEnd));
4421     else if (dim == d) PetscCall(DMPlexGetDepthStratum(dm, 1, pStart, pEnd));
4422     else {
4423       *pStart = 0;
4424       *pEnd   = 0;
4425     }
4426   } else {
4427     PetscCall(DMPlexGetDepthStratum(dm, dim, pStart, pEnd));
4428   }
4429   PetscFunctionReturn(0);
4430 }
4431 
4432 static PetscErrorCode DMGetNeighbors_Plex(DM dm, PetscInt *nranks, const PetscMPIInt *ranks[])
4433 {
4434   PetscSF            sf;
4435   PetscInt           niranks, njranks, n;
4436   const PetscMPIInt *iranks, *jranks;
4437   DM_Plex           *data = (DM_Plex *)dm->data;
4438 
4439   PetscFunctionBegin;
4440   PetscCall(DMGetPointSF(dm, &sf));
4441   if (!data->neighbors) {
4442     PetscCall(PetscSFSetUp(sf));
4443     PetscCall(PetscSFGetRootRanks(sf, &njranks, &jranks, NULL, NULL, NULL));
4444     PetscCall(PetscSFGetLeafRanks(sf, &niranks, &iranks, NULL, NULL));
4445     PetscCall(PetscMalloc1(njranks + niranks + 1, &data->neighbors));
4446     PetscCall(PetscArraycpy(data->neighbors + 1, jranks, njranks));
4447     PetscCall(PetscArraycpy(data->neighbors + njranks + 1, iranks, niranks));
4448     n = njranks + niranks;
4449     PetscCall(PetscSortRemoveDupsMPIInt(&n, data->neighbors + 1));
4450     /* The following cast should never fail: can't have more neighbors than PETSC_MPI_INT_MAX */
4451     PetscCall(PetscMPIIntCast(n, data->neighbors));
4452   }
4453   if (nranks) *nranks = data->neighbors[0];
4454   if (ranks) {
4455     if (data->neighbors[0]) *ranks = data->neighbors + 1;
4456     else *ranks = NULL;
4457   }
4458   PetscFunctionReturn(0);
4459 }
4460 
4461 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM, DM, Mat, Vec, Vec);
4462 
4463 static PetscErrorCode DMInitialize_Plex(DM dm)
4464 {
4465   PetscFunctionBegin;
4466   dm->ops->view                      = DMView_Plex;
4467   dm->ops->load                      = DMLoad_Plex;
4468   dm->ops->setfromoptions            = DMSetFromOptions_Plex;
4469   dm->ops->clone                     = DMClone_Plex;
4470   dm->ops->setup                     = DMSetUp_Plex;
4471   dm->ops->createlocalsection        = DMCreateLocalSection_Plex;
4472   dm->ops->createdefaultconstraints  = DMCreateDefaultConstraints_Plex;
4473   dm->ops->createglobalvector        = DMCreateGlobalVector_Plex;
4474   dm->ops->createlocalvector         = DMCreateLocalVector_Plex;
4475   dm->ops->getlocaltoglobalmapping   = NULL;
4476   dm->ops->createfieldis             = NULL;
4477   dm->ops->createcoordinatedm        = DMCreateCoordinateDM_Plex;
4478   dm->ops->createcoordinatefield     = DMCreateCoordinateField_Plex;
4479   dm->ops->getcoloring               = NULL;
4480   dm->ops->creatematrix              = DMCreateMatrix_Plex;
4481   dm->ops->createinterpolation       = DMCreateInterpolation_Plex;
4482   dm->ops->createmassmatrix          = DMCreateMassMatrix_Plex;
4483   dm->ops->createmassmatrixlumped    = DMCreateMassMatrixLumped_Plex;
4484   dm->ops->createinjection           = DMCreateInjection_Plex;
4485   dm->ops->refine                    = DMRefine_Plex;
4486   dm->ops->coarsen                   = DMCoarsen_Plex;
4487   dm->ops->refinehierarchy           = DMRefineHierarchy_Plex;
4488   dm->ops->coarsenhierarchy          = DMCoarsenHierarchy_Plex;
4489   dm->ops->extrude                   = DMExtrude_Plex;
4490   dm->ops->globaltolocalbegin        = NULL;
4491   dm->ops->globaltolocalend          = NULL;
4492   dm->ops->localtoglobalbegin        = NULL;
4493   dm->ops->localtoglobalend          = NULL;
4494   dm->ops->destroy                   = DMDestroy_Plex;
4495   dm->ops->createsubdm               = DMCreateSubDM_Plex;
4496   dm->ops->createsuperdm             = DMCreateSuperDM_Plex;
4497   dm->ops->getdimpoints              = DMGetDimPoints_Plex;
4498   dm->ops->locatepoints              = DMLocatePoints_Plex;
4499   dm->ops->projectfunctionlocal      = DMProjectFunctionLocal_Plex;
4500   dm->ops->projectfunctionlabellocal = DMProjectFunctionLabelLocal_Plex;
4501   dm->ops->projectfieldlocal         = DMProjectFieldLocal_Plex;
4502   dm->ops->projectfieldlabellocal    = DMProjectFieldLabelLocal_Plex;
4503   dm->ops->projectbdfieldlabellocal  = DMProjectBdFieldLabelLocal_Plex;
4504   dm->ops->computel2diff             = DMComputeL2Diff_Plex;
4505   dm->ops->computel2gradientdiff     = DMComputeL2GradientDiff_Plex;
4506   dm->ops->computel2fielddiff        = DMComputeL2FieldDiff_Plex;
4507   dm->ops->getneighbors              = DMGetNeighbors_Plex;
4508   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", DMPlexInsertBoundaryValues_Plex));
4509   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerviativeBoundaryValues_C", DMPlexInsertTimeDerivativeBoundaryValues_Plex));
4510   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", DMSetUpGLVisViewer_Plex));
4511   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", DMCreateNeumannOverlap_Plex));
4512   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", DMPlexGetOverlap_Plex));
4513   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", DMPlexDistributeGetDefault_Plex));
4514   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", DMPlexDistributeSetDefault_Plex));
4515   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", DMPlexReorderGetDefault_Plex));
4516   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", DMPlexReorderSetDefault_Plex));
4517   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", DMInterpolateSolution_Plex));
4518   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", DMPlexGetOverlap_Plex));
4519   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", DMPlexSetOverlap_Plex));
4520   PetscFunctionReturn(0);
4521 }
4522 
4523 PETSC_INTERN PetscErrorCode DMClone_Plex(DM dm, DM *newdm)
4524 {
4525   DM_Plex *mesh = (DM_Plex *)dm->data;
4526   PetscSF  face_sf;
4527 
4528   PetscFunctionBegin;
4529   mesh->refct++;
4530   (*newdm)->data = mesh;
4531   PetscCall(DMPlexGetPeriodicFaceSF(dm, &face_sf));
4532   PetscCall(DMPlexSetPeriodicFaceSF(*newdm, face_sf));
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