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