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