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