xref: /petsc/src/dm/impls/plex/plexcreate.c (revision 1b742f0101dde2a03f52d9572cb99370cb7719ff)
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, 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(DMPlexGetRefinementUniform(dm, &uniformOrig));
4392   PetscCall(PetscOptionsBoundedInt("-dm_refine_pre", "The number of refinements before distribution", "DMCreate", prerefine, &prerefine, NULL, 0));
4393   PetscCall(PetscOptionsBool("-dm_refine_remap_pre", "Flag to control coordinate remapping", "DMCreate", remap, &remap, NULL));
4394   PetscCall(PetscOptionsBool("-dm_refine_uniform_pre", "Flag for uniform refinement before distribution", "DMCreate", uniform, &uniform, &flg));
4395   if (flg) PetscCall(DMPlexSetRefinementUniform(dm, uniform));
4396   PetscCall(PetscOptionsReal("-dm_refine_volume_limit_pre", "The maximum cell volume after refinement before distribution", "DMCreate", volume, &volume, &flg));
4397   if (flg) {
4398     PetscCall(DMPlexSetRefinementUniform(dm, PETSC_FALSE));
4399     PetscCall(DMPlexSetRefinementLimit(dm, volume));
4400     prerefine = PetscMax(prerefine, 1);
4401   }
4402   if (prerefine) PetscCall(DMLocalizeCoordinates(dm));
4403   for (r = 0; r < prerefine; ++r) {
4404     DM             rdm;
4405     PetscPointFunc coordFunc = ((DM_Plex *)dm->data)->coordFunc;
4406 
4407     PetscCall(DMSetFromOptions_NonRefinement_Plex(dm, PetscOptionsObject));
4408     PetscCall(DMRefine(dm, PetscObjectComm((PetscObject)dm), &rdm));
4409     PetscCall(DMPlexReplace_Internal(dm, &rdm));
4410     PetscCall(DMSetFromOptions_NonRefinement_Plex(dm, PetscOptionsObject));
4411     if (coordFunc && remap) {
4412       PetscCall(DMPlexRemapGeometry(dm, 0.0, coordFunc));
4413       ((DM_Plex *)dm->data)->coordFunc = coordFunc;
4414     }
4415   }
4416   PetscCall(DMPlexSetRefinementUniform(dm, uniformOrig));
4417   /* Handle DMPlex extrusion before distribution */
4418   PetscCall(PetscOptionsBoundedInt("-dm_extrude", "The number of layers to extrude", "", extLayers, &extLayers, NULL, 0));
4419   if (extLayers) {
4420     DM edm;
4421 
4422     PetscCall(DMExtrude(dm, extLayers, &edm));
4423     PetscCall(DMPlexReplace_Internal(dm, &edm));
4424     ((DM_Plex *)dm->data)->coordFunc = NULL;
4425     PetscCall(DMSetFromOptions_NonRefinement_Plex(dm, PetscOptionsObject));
4426     extLayers = 0;
4427     PetscCall(DMGetDimension(dm, &dim));
4428   }
4429   /* Handle DMPlex reordering before distribution */
4430   PetscCall(DMPlexReorderGetDefault(dm, &reorder));
4431   PetscCall(MatGetOrderingList(&ordlist));
4432   PetscCall(PetscStrncpy(oname, MATORDERINGNATURAL, sizeof(oname)));
4433   PetscCall(PetscOptionsFList("-dm_plex_reorder", "Set mesh reordering type", "DMPlexGetOrdering", ordlist, MATORDERINGNATURAL, oname, sizeof(oname), &flg));
4434   if (reorder == DM_REORDER_DEFAULT_TRUE || flg) {
4435     DM pdm;
4436     IS perm;
4437 
4438     PetscCall(DMPlexGetOrdering(dm, oname, NULL, &perm));
4439     PetscCall(DMPlexPermute(dm, perm, &pdm));
4440     PetscCall(ISDestroy(&perm));
4441     PetscCall(DMPlexReplace_Internal(dm, &pdm));
4442     PetscCall(DMSetFromOptions_NonRefinement_Plex(dm, PetscOptionsObject));
4443   }
4444   /* Handle DMPlex distribution */
4445   PetscCall(DMPlexDistributeGetDefault(dm, &distribute));
4446   PetscCall(PetscOptionsBool("-dm_distribute", "Flag to redistribute a mesh among processes", "DMPlexDistribute", distribute, &distribute, NULL));
4447   PetscCall(PetscOptionsBool("-dm_distribute_save_sf", "Flag to save the migration SF", "DMPlexSetMigrationSF", saveSF, &saveSF, NULL));
4448   PetscCall(DMSetFromOptions_Overlap_Plex(dm, PetscOptionsObject, &overlap));
4449   if (distribute) {
4450     DM               pdm = NULL;
4451     PetscPartitioner part;
4452     PetscSF          sfMigration;
4453 
4454     PetscCall(DMPlexGetPartitioner(dm, &part));
4455     PetscCall(PetscPartitionerSetFromOptions(part));
4456     PetscCall(DMPlexDistribute(dm, overlap, &sfMigration, &pdm));
4457     if (pdm) PetscCall(DMPlexReplace_Internal(dm, &pdm));
4458     if (saveSF) PetscCall(DMPlexSetMigrationSF(dm, sfMigration));
4459     PetscCall(PetscSFDestroy(&sfMigration));
4460   }
4461   /* Must check CEED options before creating function space for coordinates */
4462   {
4463     PetscBool useCeed = PETSC_FALSE, flg;
4464 
4465     PetscCall(PetscOptionsBool("-dm_plex_use_ceed", "Use LibCEED as the FEM backend", "DMPlexSetUseCeed", useCeed, &useCeed, &flg));
4466     if (flg) PetscCall(DMPlexSetUseCeed(dm, useCeed));
4467   }
4468   /* Create coordinate space */
4469   if (created) {
4470     DM_Plex  *mesh   = (DM_Plex *)dm->data;
4471     PetscInt  degree = 1, deg;
4472     PetscInt  height = 0;
4473     DM        cdm;
4474     PetscBool flg, localize = PETSC_TRUE, sparseLocalize = PETSC_TRUE;
4475 
4476     PetscCall(PetscOptionsBool("-dm_coord_space", "Use an FEM space for coordinates", "", coordSpace, &coordSpace, &flg));
4477     PetscCall(PetscOptionsInt("-dm_coord_petscspace_degree", "FEM degree for coordinate space", "", degree, &degree, NULL));
4478     PetscCall(DMGetCoordinateDegree_Internal(dm, &deg));
4479     if (coordSpace && deg <= 1) PetscCall(DMPlexCreateCoordinateSpace(dm, degree, PETSC_TRUE, mesh->coordFunc));
4480     PetscCall(DMGetCoordinateDM(dm, &cdm));
4481     if (flg && !coordSpace) {
4482       PetscDS      cds;
4483       PetscObject  obj;
4484       PetscClassId id;
4485 
4486       PetscCall(DMGetDS(cdm, &cds));
4487       PetscCall(PetscDSGetDiscretization(cds, 0, &obj));
4488       PetscCall(PetscObjectGetClassId(obj, &id));
4489       if (id == PETSCFE_CLASSID) {
4490         PetscContainer dummy;
4491 
4492         PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &dummy));
4493         PetscCall(PetscObjectSetName((PetscObject)dummy, "coordinates"));
4494         PetscCall(DMSetField(cdm, 0, NULL, (PetscObject)dummy));
4495         PetscCall(PetscContainerDestroy(&dummy));
4496         PetscCall(DMClearDS(cdm));
4497       }
4498       mesh->coordFunc = NULL;
4499     }
4500     PetscCall(PetscOptionsBool("-dm_localize", "Localize mesh coordinates", "", localize, &localize, NULL));
4501     PetscCall(PetscOptionsBool("-dm_sparse_localize", "Localize only necessary cells", "DMSetSparseLocalize", sparseLocalize, &sparseLocalize, &flg));
4502     if (flg) PetscCall(DMSetSparseLocalize(dm, sparseLocalize));
4503     PetscCall(PetscOptionsInt("-dm_localize_height", "Localize edges and faces in addition to cells", "", height, &height, &flg));
4504     if (flg) PetscCall(DMPlexSetMaxProjectionHeight(cdm, height));
4505     if (localize) PetscCall(DMLocalizeCoordinates(dm));
4506   }
4507   /* Handle DMPlex refinement */
4508   remap = PETSC_TRUE;
4509   PetscCall(PetscOptionsBoundedInt("-dm_refine", "The number of uniform refinements", "DMCreate", refine, &refine, NULL, 0));
4510   PetscCall(PetscOptionsBool("-dm_refine_remap", "Flag to control coordinate remapping", "DMCreate", remap, &remap, NULL));
4511   PetscCall(PetscOptionsBoundedInt("-dm_refine_hierarchy", "The number of uniform refinements", "DMCreate", refine, &refine, &isHierarchy, 0));
4512   if (refine) PetscCall(DMPlexSetRefinementUniform(dm, PETSC_TRUE));
4513   if (refine && isHierarchy) {
4514     DM *dms, coarseDM;
4515 
4516     PetscCall(DMGetCoarseDM(dm, &coarseDM));
4517     PetscCall(PetscObjectReference((PetscObject)coarseDM));
4518     PetscCall(PetscMalloc1(refine, &dms));
4519     PetscCall(DMRefineHierarchy(dm, refine, dms));
4520     /* Total hack since we do not pass in a pointer */
4521     PetscCall(DMPlexSwap_Static(dm, dms[refine - 1]));
4522     if (refine == 1) {
4523       PetscCall(DMSetCoarseDM(dm, dms[0]));
4524       PetscCall(DMPlexSetRegularRefinement(dm, PETSC_TRUE));
4525     } else {
4526       PetscCall(DMSetCoarseDM(dm, dms[refine - 2]));
4527       PetscCall(DMPlexSetRegularRefinement(dm, PETSC_TRUE));
4528       PetscCall(DMSetCoarseDM(dms[0], dms[refine - 1]));
4529       PetscCall(DMPlexSetRegularRefinement(dms[0], PETSC_TRUE));
4530     }
4531     PetscCall(DMSetCoarseDM(dms[refine - 1], coarseDM));
4532     PetscCall(PetscObjectDereference((PetscObject)coarseDM));
4533     /* Free DMs */
4534     for (r = 0; r < refine; ++r) {
4535       PetscCall(DMSetFromOptions_NonRefinement_Plex(dms[r], PetscOptionsObject));
4536       PetscCall(DMDestroy(&dms[r]));
4537     }
4538     PetscCall(PetscFree(dms));
4539   } else {
4540     for (r = 0; r < refine; ++r) {
4541       DM             rdm;
4542       PetscPointFunc coordFunc = ((DM_Plex *)dm->data)->coordFunc;
4543 
4544       PetscCall(DMSetFromOptions_NonRefinement_Plex(dm, PetscOptionsObject));
4545       PetscCall(DMRefine(dm, PetscObjectComm((PetscObject)dm), &rdm));
4546       /* Total hack since we do not pass in a pointer */
4547       PetscCall(DMPlexReplace_Internal(dm, &rdm));
4548       PetscCall(DMSetFromOptions_NonRefinement_Plex(dm, PetscOptionsObject));
4549       if (coordFunc && remap) {
4550         PetscCall(DMPlexRemapGeometry(dm, 0.0, coordFunc));
4551         ((DM_Plex *)dm->data)->coordFunc = coordFunc;
4552       }
4553     }
4554   }
4555   /* Handle DMPlex coarsening */
4556   PetscCall(PetscOptionsBoundedInt("-dm_coarsen", "Coarsen the mesh", "DMCreate", coarsen, &coarsen, NULL, 0));
4557   PetscCall(PetscOptionsBoundedInt("-dm_coarsen_hierarchy", "The number of coarsenings", "DMCreate", coarsen, &coarsen, &isHierarchy, 0));
4558   if (coarsen && isHierarchy) {
4559     DM *dms;
4560 
4561     PetscCall(PetscMalloc1(coarsen, &dms));
4562     PetscCall(DMCoarsenHierarchy(dm, coarsen, dms));
4563     /* Free DMs */
4564     for (r = 0; r < coarsen; ++r) {
4565       PetscCall(DMSetFromOptions_NonRefinement_Plex(dms[r], PetscOptionsObject));
4566       PetscCall(DMDestroy(&dms[r]));
4567     }
4568     PetscCall(PetscFree(dms));
4569   } else {
4570     for (r = 0; r < coarsen; ++r) {
4571       DM             cdm;
4572       PetscPointFunc coordFunc = ((DM_Plex *)dm->data)->coordFunc;
4573 
4574       PetscCall(DMSetFromOptions_NonRefinement_Plex(dm, PetscOptionsObject));
4575       PetscCall(DMCoarsen(dm, PetscObjectComm((PetscObject)dm), &cdm));
4576       /* Total hack since we do not pass in a pointer */
4577       PetscCall(DMPlexReplace_Internal(dm, &cdm));
4578       PetscCall(DMSetFromOptions_NonRefinement_Plex(dm, PetscOptionsObject));
4579       if (coordFunc) {
4580         PetscCall(DMPlexRemapGeometry(dm, 0.0, coordFunc));
4581         ((DM_Plex *)dm->data)->coordFunc = coordFunc;
4582       }
4583     }
4584   }
4585   // Handle coordinate remapping
4586   remap = PETSC_FALSE;
4587   PetscCall(PetscOptionsBool("-dm_coord_remap", "Flag to control coordinate remapping", "", remap, &remap, NULL));
4588   if (remap) {
4589     DMPlexCoordMap map     = DM_COORD_MAP_NONE;
4590     PetscPointFunc mapFunc = NULL;
4591     PetscScalar    params[16];
4592     PetscInt       Np = PETSC_STATIC_ARRAY_LENGTH(params), cdim;
4593     MPI_Comm       comm;
4594 
4595     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
4596     PetscCall(DMGetCoordinateDim(dm, &cdim));
4597     PetscCall(PetscOptionsScalarArray("-dm_coord_map_params", "Parameters for the coordinate remapping", "", params, &Np, &flg));
4598     if (!flg) Np = 0;
4599     // TODO Allow user to pass a map function by name
4600     PetscCall(PetscOptionsEnum("-dm_coord_map", "Coordinate mapping for built-in mesh", "", DMPlexCoordMaps, (PetscEnum)map, (PetscEnum *)&map, &flg));
4601     if (flg) {
4602       switch (map) {
4603       case DM_COORD_MAP_NONE:
4604         mapFunc = coordMap_identity;
4605         break;
4606       case DM_COORD_MAP_SHEAR:
4607         mapFunc = coordMap_shear;
4608         if (!Np) {
4609           Np        = cdim + 1;
4610           params[0] = 0;
4611           for (PetscInt d = 1; d <= cdim; ++d) params[d] = 1.0;
4612         }
4613         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);
4614         break;
4615       case DM_COORD_MAP_FLARE:
4616         mapFunc = coordMap_flare;
4617         if (!Np) {
4618           Np        = cdim + 1;
4619           params[0] = 0;
4620           for (PetscInt d = 1; d <= cdim; ++d) params[d] = 1.0;
4621         }
4622         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);
4623         break;
4624       case DM_COORD_MAP_ANNULUS:
4625         mapFunc = coordMap_annulus;
4626         if (!Np) {
4627           Np        = 2;
4628           params[0] = 1.;
4629           params[1] = 2.;
4630         }
4631         PetscCheck(Np == 2, comm, PETSC_ERR_ARG_WRONG, "The annulus coordinate map must have 2 parameters, not %" PetscInt_FMT, Np);
4632         break;
4633       case DM_COORD_MAP_SHELL:
4634         mapFunc = coordMap_shell;
4635         if (!Np) {
4636           Np        = 2;
4637           params[0] = 1.;
4638           params[1] = 2.;
4639         }
4640         PetscCheck(Np == 2, comm, PETSC_ERR_ARG_WRONG, "The spherical shell coordinate map must have 2 parameters, not %" PetscInt_FMT, Np);
4641         break;
4642       default:
4643         mapFunc = coordMap_identity;
4644       }
4645     }
4646     if (Np) {
4647       DM      cdm;
4648       PetscDS cds;
4649 
4650       PetscCall(DMGetCoordinateDM(dm, &cdm));
4651       PetscCall(DMGetDS(cdm, &cds));
4652       PetscCall(PetscDSSetConstants(cds, Np, params));
4653     }
4654     PetscCall(DMPlexRemapGeometry(dm, 0.0, mapFunc));
4655   }
4656   /* Handle ghost cells */
4657   PetscCall(PetscOptionsBool("-dm_plex_create_fv_ghost_cells", "Flag to create finite volume ghost cells on the boundary", "DMCreate", ghostCells, &ghostCells, NULL));
4658   if (ghostCells) {
4659     DM   gdm;
4660     char lname[PETSC_MAX_PATH_LEN];
4661 
4662     lname[0] = '\0';
4663     PetscCall(PetscOptionsString("-dm_plex_fv_ghost_cells_label", "Label name for ghost cells boundary", "DMCreate", lname, lname, sizeof(lname), &flg));
4664     PetscCall(DMPlexConstructGhostCells(dm, flg ? lname : NULL, NULL, &gdm));
4665     PetscCall(DMPlexReplace_Internal(dm, &gdm));
4666   }
4667   /* Handle 1D order */
4668   if (reorder != DM_REORDER_DEFAULT_FALSE && dim == 1) {
4669     DM           cdm, rdm;
4670     PetscDS      cds;
4671     PetscObject  obj;
4672     PetscClassId id = PETSC_OBJECT_CLASSID;
4673     IS           perm;
4674     PetscInt     Nf;
4675     PetscBool    distributed;
4676 
4677     PetscCall(DMPlexIsDistributed(dm, &distributed));
4678     PetscCall(DMGetCoordinateDM(dm, &cdm));
4679     PetscCall(DMGetDS(cdm, &cds));
4680     PetscCall(PetscDSGetNumFields(cds, &Nf));
4681     if (Nf) {
4682       PetscCall(PetscDSGetDiscretization(cds, 0, &obj));
4683       PetscCall(PetscObjectGetClassId(obj, &id));
4684     }
4685     if (!distributed && id != PETSCFE_CLASSID) {
4686       PetscCall(DMPlexGetOrdering1D(dm, &perm));
4687       PetscCall(DMPlexPermute(dm, perm, &rdm));
4688       PetscCall(DMPlexReplace_Internal(dm, &rdm));
4689       PetscCall(ISDestroy(&perm));
4690     }
4691   }
4692 /* Handle */
4693 non_refine:
4694   PetscCall(DMSetFromOptions_NonRefinement_Plex(dm, PetscOptionsObject));
4695   PetscOptionsHeadEnd();
4696   PetscFunctionReturn(PETSC_SUCCESS);
4697 }
4698 
4699 static PetscErrorCode DMCreateGlobalVector_Plex(DM dm, Vec *vec)
4700 {
4701   PetscFunctionBegin;
4702   PetscCall(DMCreateGlobalVector_Section_Private(dm, vec));
4703   /* PetscCall(VecSetOperation(*vec, VECOP_DUPLICATE, (void(*)(void)) VecDuplicate_MPI_DM)); */
4704   PetscCall(VecSetOperation(*vec, VECOP_VIEW, (void (*)(void))VecView_Plex));
4705   PetscCall(VecSetOperation(*vec, VECOP_VIEWNATIVE, (void (*)(void))VecView_Plex_Native));
4706   PetscCall(VecSetOperation(*vec, VECOP_LOAD, (void (*)(void))VecLoad_Plex));
4707   PetscCall(VecSetOperation(*vec, VECOP_LOADNATIVE, (void (*)(void))VecLoad_Plex_Native));
4708   PetscFunctionReturn(PETSC_SUCCESS);
4709 }
4710 
4711 static PetscErrorCode DMCreateLocalVector_Plex(DM dm, Vec *vec)
4712 {
4713   PetscFunctionBegin;
4714   PetscCall(DMCreateLocalVector_Section_Private(dm, vec));
4715   PetscCall(VecSetOperation(*vec, VECOP_VIEW, (void (*)(void))VecView_Plex_Local));
4716   PetscCall(VecSetOperation(*vec, VECOP_LOAD, (void (*)(void))VecLoad_Plex_Local));
4717   PetscFunctionReturn(PETSC_SUCCESS);
4718 }
4719 
4720 static PetscErrorCode DMGetDimPoints_Plex(DM dm, PetscInt dim, PetscInt *pStart, PetscInt *pEnd)
4721 {
4722   PetscInt depth, d;
4723 
4724   PetscFunctionBegin;
4725   PetscCall(DMPlexGetDepth(dm, &depth));
4726   if (depth == 1) {
4727     PetscCall(DMGetDimension(dm, &d));
4728     if (dim == 0) PetscCall(DMPlexGetDepthStratum(dm, dim, pStart, pEnd));
4729     else if (dim == d) PetscCall(DMPlexGetDepthStratum(dm, 1, pStart, pEnd));
4730     else {
4731       *pStart = 0;
4732       *pEnd   = 0;
4733     }
4734   } else {
4735     PetscCall(DMPlexGetDepthStratum(dm, dim, pStart, pEnd));
4736   }
4737   PetscFunctionReturn(PETSC_SUCCESS);
4738 }
4739 
4740 static PetscErrorCode DMGetNeighbors_Plex(DM dm, PetscInt *nranks, const PetscMPIInt *ranks[])
4741 {
4742   PetscSF            sf;
4743   PetscInt           niranks, njranks, n;
4744   const PetscMPIInt *iranks, *jranks;
4745   DM_Plex           *data = (DM_Plex *)dm->data;
4746 
4747   PetscFunctionBegin;
4748   PetscCall(DMGetPointSF(dm, &sf));
4749   if (!data->neighbors) {
4750     PetscCall(PetscSFSetUp(sf));
4751     PetscCall(PetscSFGetRootRanks(sf, &njranks, &jranks, NULL, NULL, NULL));
4752     PetscCall(PetscSFGetLeafRanks(sf, &niranks, &iranks, NULL, NULL));
4753     PetscCall(PetscMalloc1(njranks + niranks + 1, &data->neighbors));
4754     PetscCall(PetscArraycpy(data->neighbors + 1, jranks, njranks));
4755     PetscCall(PetscArraycpy(data->neighbors + njranks + 1, iranks, niranks));
4756     n = njranks + niranks;
4757     PetscCall(PetscSortRemoveDupsMPIInt(&n, data->neighbors + 1));
4758     /* The following cast should never fail: can't have more neighbors than PETSC_MPI_INT_MAX */
4759     PetscCall(PetscMPIIntCast(n, data->neighbors));
4760   }
4761   if (nranks) *nranks = data->neighbors[0];
4762   if (ranks) {
4763     if (data->neighbors[0]) *ranks = data->neighbors + 1;
4764     else *ranks = NULL;
4765   }
4766   PetscFunctionReturn(PETSC_SUCCESS);
4767 }
4768 
4769 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM, DM, Mat, Vec, Vec);
4770 
4771 static PetscErrorCode DMInitialize_Plex(DM dm)
4772 {
4773   PetscFunctionBegin;
4774   dm->ops->view                      = DMView_Plex;
4775   dm->ops->load                      = DMLoad_Plex;
4776   dm->ops->setfromoptions            = DMSetFromOptions_Plex;
4777   dm->ops->clone                     = DMClone_Plex;
4778   dm->ops->setup                     = DMSetUp_Plex;
4779   dm->ops->createlocalsection        = DMCreateLocalSection_Plex;
4780   dm->ops->createsectionpermutation  = DMCreateSectionPermutation_Plex;
4781   dm->ops->createdefaultconstraints  = DMCreateDefaultConstraints_Plex;
4782   dm->ops->createglobalvector        = DMCreateGlobalVector_Plex;
4783   dm->ops->createlocalvector         = DMCreateLocalVector_Plex;
4784   dm->ops->getlocaltoglobalmapping   = NULL;
4785   dm->ops->createfieldis             = NULL;
4786   dm->ops->createcoordinatedm        = DMCreateCoordinateDM_Plex;
4787   dm->ops->createcoordinatefield     = DMCreateCoordinateField_Plex;
4788   dm->ops->getcoloring               = NULL;
4789   dm->ops->creatematrix              = DMCreateMatrix_Plex;
4790   dm->ops->createinterpolation       = DMCreateInterpolation_Plex;
4791   dm->ops->createmassmatrix          = DMCreateMassMatrix_Plex;
4792   dm->ops->createmassmatrixlumped    = DMCreateMassMatrixLumped_Plex;
4793   dm->ops->createinjection           = DMCreateInjection_Plex;
4794   dm->ops->refine                    = DMRefine_Plex;
4795   dm->ops->coarsen                   = DMCoarsen_Plex;
4796   dm->ops->refinehierarchy           = DMRefineHierarchy_Plex;
4797   dm->ops->coarsenhierarchy          = DMCoarsenHierarchy_Plex;
4798   dm->ops->extrude                   = DMExtrude_Plex;
4799   dm->ops->globaltolocalbegin        = NULL;
4800   dm->ops->globaltolocalend          = NULL;
4801   dm->ops->localtoglobalbegin        = NULL;
4802   dm->ops->localtoglobalend          = NULL;
4803   dm->ops->destroy                   = DMDestroy_Plex;
4804   dm->ops->createsubdm               = DMCreateSubDM_Plex;
4805   dm->ops->createsuperdm             = DMCreateSuperDM_Plex;
4806   dm->ops->getdimpoints              = DMGetDimPoints_Plex;
4807   dm->ops->locatepoints              = DMLocatePoints_Plex;
4808   dm->ops->projectfunctionlocal      = DMProjectFunctionLocal_Plex;
4809   dm->ops->projectfunctionlabellocal = DMProjectFunctionLabelLocal_Plex;
4810   dm->ops->projectfieldlocal         = DMProjectFieldLocal_Plex;
4811   dm->ops->projectfieldlabellocal    = DMProjectFieldLabelLocal_Plex;
4812   dm->ops->projectbdfieldlabellocal  = DMProjectBdFieldLabelLocal_Plex;
4813   dm->ops->computel2diff             = DMComputeL2Diff_Plex;
4814   dm->ops->computel2gradientdiff     = DMComputeL2GradientDiff_Plex;
4815   dm->ops->computel2fielddiff        = DMComputeL2FieldDiff_Plex;
4816   dm->ops->getneighbors              = DMGetNeighbors_Plex;
4817   dm->ops->getlocalboundingbox       = DMGetLocalBoundingBox_Coordinates;
4818   dm->ops->createdomaindecomposition = DMCreateDomainDecomposition_Plex;
4819   dm->ops->createddscatters          = DMCreateDomainDecompositionScatters_Plex;
4820   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", DMPlexInsertBoundaryValues_Plex));
4821   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerivativeBoundaryValues_C", DMPlexInsertTimeDerivativeBoundaryValues_Plex));
4822   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", DMSetUpGLVisViewer_Plex));
4823   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", DMCreateNeumannOverlap_Plex));
4824   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", DMPlexDistributeGetDefault_Plex));
4825   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", DMPlexDistributeSetDefault_Plex));
4826   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", DMPlexReorderGetDefault_Plex));
4827   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", DMPlexReorderSetDefault_Plex));
4828   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetDefault_C", DMReorderSectionGetDefault_Plex));
4829   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetDefault_C", DMReorderSectionSetDefault_Plex));
4830   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetType_C", DMReorderSectionGetType_Plex));
4831   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetType_C", DMReorderSectionSetType_Plex));
4832   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", DMInterpolateSolution_Plex));
4833   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", DMPlexGetOverlap_Plex));
4834   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", DMPlexSetOverlap_Plex));
4835   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetUseCeed_C", DMPlexGetUseCeed_Plex));
4836   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetUseCeed_C", DMPlexSetUseCeed_Plex));
4837   PetscFunctionReturn(PETSC_SUCCESS);
4838 }
4839 
4840 PETSC_INTERN PetscErrorCode DMClone_Plex(DM dm, DM *newdm)
4841 {
4842   DM_Plex       *mesh = (DM_Plex *)dm->data;
4843   const PetscSF *face_sfs;
4844   PetscInt       num_face_sfs;
4845 
4846   PetscFunctionBegin;
4847   mesh->refct++;
4848   (*newdm)->data = mesh;
4849   PetscCall(DMPlexGetIsoperiodicFaceSF(dm, &num_face_sfs, &face_sfs));
4850   PetscCall(DMPlexSetIsoperiodicFaceSF(*newdm, num_face_sfs, (PetscSF *)face_sfs));
4851   PetscCall(PetscObjectChangeTypeName((PetscObject)*newdm, DMPLEX));
4852   PetscCall(DMInitialize_Plex(*newdm));
4853   PetscFunctionReturn(PETSC_SUCCESS);
4854 }
4855 
4856 /*MC
4857   DMPLEX = "plex" - A `DM` object that encapsulates an unstructured mesh, or CW Complex, which can be expressed using a Hasse Diagram.
4858                     In the local representation, `Vec`s contain all unknowns in the interior and shared boundary. This is
4859                     specified by a PetscSection object. Ownership in the global representation is determined by
4860                     ownership of the underlying `DMPLEX` points. This is specified by another `PetscSection` object.
4861 
4862   Options Database Keys:
4863 + -dm_refine_pre                     - Refine mesh before distribution
4864 + -dm_refine_uniform_pre             - Choose uniform or generator-based refinement
4865 + -dm_refine_volume_limit_pre        - Cell volume limit after pre-refinement using generator
4866 . -dm_distribute                     - Distribute mesh across processes
4867 . -dm_distribute_overlap             - Number of cells to overlap for distribution
4868 . -dm_refine                         - Refine mesh after distribution
4869 . -dm_localize <bool>                - Whether to localize coordinates for periodic meshes
4870 . -dm_sparse_localize <bool>         - Whether to only localize cells on the periodic boundary
4871 . -dm_plex_hash_location             - Use grid hashing for point location
4872 . -dm_plex_hash_box_faces <n,m,p>    - The number of divisions in each direction of the grid hash
4873 . -dm_plex_partition_balance         - Attempt to evenly divide points on partition boundary between processes
4874 . -dm_plex_remesh_bd                 - Allow changes to the boundary on remeshing
4875 . -dm_plex_max_projection_height     - Maximum mesh point height used to project locally
4876 . -dm_plex_regular_refinement        - Use special nested projection algorithm for regular refinement
4877 . -dm_plex_reorder_section           - Use specialized blocking if available
4878 . -dm_plex_check_all                 - Perform all checks below
4879 . -dm_plex_check_symmetry            - Check that the adjacency information in the mesh is symmetric
4880 . -dm_plex_check_skeleton <celltype> - Check that each cell has the correct number of vertices
4881 . -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
4882 . -dm_plex_check_geometry            - Check that cells have positive volume
4883 . -dm_view :mesh.tex:ascii_latex     - View the mesh in LaTeX/TikZ
4884 . -dm_plex_view_scale <num>          - Scale the TikZ
4885 . -dm_plex_print_fem <num>           - View FEM assembly information, such as element vectors and matrices
4886 - -dm_plex_print_fvm <num>           - View FVM assembly information, such as flux updates
4887 
4888   Level: intermediate
4889 
4890 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMType`, `DMPlexCreate()`, `DMCreate()`, `DMSetType()`, `PetscSection`
4891 M*/
4892 
4893 PETSC_EXTERN PetscErrorCode DMCreate_Plex(DM dm)
4894 {
4895   DM_Plex *mesh;
4896   PetscInt unit;
4897 
4898   PetscFunctionBegin;
4899   PetscCall(PetscCitationsRegister(PlexCitation, &Plexcite));
4900   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4901   PetscCall(PetscNew(&mesh));
4902   dm->reorderSection = DM_REORDER_DEFAULT_NOTSET;
4903   dm->data           = mesh;
4904 
4905   mesh->refct = 1;
4906   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &mesh->coneSection));
4907   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &mesh->supportSection));
4908   mesh->refinementUniform      = PETSC_TRUE;
4909   mesh->refinementLimit        = -1.0;
4910   mesh->distDefault            = PETSC_TRUE;
4911   mesh->reorderDefault         = DM_REORDER_DEFAULT_NOTSET;
4912   mesh->distributionName       = NULL;
4913   mesh->interpolated           = DMPLEX_INTERPOLATED_INVALID;
4914   mesh->interpolatedCollective = DMPLEX_INTERPOLATED_INVALID;
4915 
4916   PetscCall(PetscPartitionerCreate(PetscObjectComm((PetscObject)dm), &mesh->partitioner));
4917   mesh->remeshBd = PETSC_FALSE;
4918 
4919   for (unit = 0; unit < NUM_PETSC_UNITS; ++unit) mesh->scale[unit] = 1.0;
4920 
4921   mesh->depthState    = -1;
4922   mesh->celltypeState = -1;
4923   mesh->printTol      = 1.0e-10;
4924 
4925   PetscCall(DMInitialize_Plex(dm));
4926   PetscFunctionReturn(PETSC_SUCCESS);
4927 }
4928 
4929 /*@
4930   DMPlexCreate - Creates a `DMPLEX` object, which encapsulates an unstructured mesh, or CW complex, which can be expressed using a Hasse Diagram.
4931 
4932   Collective
4933 
4934   Input Parameter:
4935 . comm - The communicator for the `DMPLEX` object
4936 
4937   Output Parameter:
4938 . mesh - The `DMPLEX` object
4939 
4940   Level: beginner
4941 
4942 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMType`, `DMCreate()`, `DMSetType()`
4943 @*/
4944 PetscErrorCode DMPlexCreate(MPI_Comm comm, DM *mesh)
4945 {
4946   PetscFunctionBegin;
4947   PetscAssertPointer(mesh, 2);
4948   PetscCall(DMCreate(comm, mesh));
4949   PetscCall(DMSetType(*mesh, DMPLEX));
4950   PetscFunctionReturn(PETSC_SUCCESS);
4951 }
4952 
4953 /*@C
4954   DMPlexBuildFromCellListParallel - Build distributed `DMPLEX` topology from a list of vertices for each cell (common mesh generator output)
4955 
4956   Collective; No Fortran Support
4957 
4958   Input Parameters:
4959 + dm          - The `DM`
4960 . numCells    - The number of cells owned by this process
4961 . numVertices - The number of vertices to be owned by this process, or `PETSC_DECIDE`
4962 . NVertices   - The global number of vertices, or `PETSC_DETERMINE`
4963 . numCorners  - The number of vertices for each cell
4964 - cells       - An array of numCells*numCorners numbers, the global vertex numbers for each cell
4965 
4966   Output Parameters:
4967 + vertexSF         - (Optional) `PetscSF` describing complete vertex ownership
4968 - verticesAdjSaved - (Optional) vertex adjacency array
4969 
4970   Level: advanced
4971 
4972   Notes:
4973   Two triangles sharing a face
4974 .vb
4975 
4976         2
4977       / | \
4978      /  |  \
4979     /   |   \
4980    0  0 | 1  3
4981     \   |   /
4982      \  |  /
4983       \ | /
4984         1
4985 .ve
4986   would have input
4987 .vb
4988   numCells = 2, numVertices = 4
4989   cells = [0 1 2  1 3 2]
4990 .ve
4991   which would result in the `DMPLEX`
4992 .vb
4993 
4994         4
4995       / | \
4996      /  |  \
4997     /   |   \
4998    2  0 | 1  5
4999     \   |   /
5000      \  |  /
5001       \ | /
5002         3
5003 .ve
5004 
5005   Vertices are implicitly numbered consecutively 0,...,NVertices.
5006   Each rank owns a chunk of numVertices consecutive vertices.
5007   If numVertices is `PETSC_DECIDE`, PETSc will distribute them as evenly as possible using PetscLayout.
5008   If NVertices is `PETSC_DETERMINE` and numVertices is PETSC_DECIDE, NVertices is computed by PETSc as the maximum vertex index in cells + 1.
5009   If only NVertices is `PETSC_DETERMINE`, it is computed as the sum of numVertices over all ranks.
5010 
5011   The cell distribution is arbitrary non-overlapping, independent of the vertex distribution.
5012 
5013 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexBuildFromCellList()`, `DMPlexCreateFromCellListParallelPetsc()`, `DMPlexBuildCoordinatesFromCellListParallel()`,
5014           `PetscSF`
5015 @*/
5016 PetscErrorCode DMPlexBuildFromCellListParallel(DM dm, PetscInt numCells, PetscInt numVertices, PetscInt NVertices, PetscInt numCorners, const PetscInt cells[], PetscSF *vertexSF, PetscInt **verticesAdjSaved)
5017 {
5018   PetscSF     sfPoint;
5019   PetscLayout layout;
5020   PetscInt    numVerticesAdj, *verticesAdj, *cones, c, p;
5021 
5022   PetscFunctionBegin;
5023   PetscValidLogicalCollectiveInt(dm, NVertices, 4);
5024   PetscCall(PetscLogEventBegin(DMPLEX_BuildFromCellList, dm, 0, 0, 0));
5025   /* Get/check global number of vertices */
5026   {
5027     PetscInt       NVerticesInCells, i;
5028     const PetscInt len = numCells * numCorners;
5029 
5030     /* NVerticesInCells = max(cells) + 1 */
5031     NVerticesInCells = PETSC_MIN_INT;
5032     for (i = 0; i < len; i++)
5033       if (cells[i] > NVerticesInCells) NVerticesInCells = cells[i];
5034     ++NVerticesInCells;
5035     PetscCall(MPIU_Allreduce(MPI_IN_PLACE, &NVerticesInCells, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
5036 
5037     if (numVertices == PETSC_DECIDE && NVertices == PETSC_DECIDE) NVertices = NVerticesInCells;
5038     else
5039       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);
5040   }
5041   /* Count locally unique vertices */
5042   {
5043     PetscHSetI vhash;
5044     PetscInt   off = 0;
5045 
5046     PetscCall(PetscHSetICreate(&vhash));
5047     for (c = 0; c < numCells; ++c) {
5048       for (p = 0; p < numCorners; ++p) PetscCall(PetscHSetIAdd(vhash, cells[c * numCorners + p]));
5049     }
5050     PetscCall(PetscHSetIGetSize(vhash, &numVerticesAdj));
5051     if (!verticesAdjSaved) PetscCall(PetscMalloc1(numVerticesAdj, &verticesAdj));
5052     else verticesAdj = *verticesAdjSaved;
5053     PetscCall(PetscHSetIGetElems(vhash, &off, verticesAdj));
5054     PetscCall(PetscHSetIDestroy(&vhash));
5055     PetscCheck(off == numVerticesAdj, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Invalid number of local vertices %" PetscInt_FMT " should be %" PetscInt_FMT, off, numVerticesAdj);
5056   }
5057   PetscCall(PetscSortInt(numVerticesAdj, verticesAdj));
5058   /* Create cones */
5059   PetscCall(DMPlexSetChart(dm, 0, numCells + numVerticesAdj));
5060   for (c = 0; c < numCells; ++c) PetscCall(DMPlexSetConeSize(dm, c, numCorners));
5061   PetscCall(DMSetUp(dm));
5062   PetscCall(DMPlexGetCones(dm, &cones));
5063   for (c = 0; c < numCells; ++c) {
5064     for (p = 0; p < numCorners; ++p) {
5065       const PetscInt gv = cells[c * numCorners + p];
5066       PetscInt       lv;
5067 
5068       /* Positions within verticesAdj form 0-based local vertex numbering;
5069          we need to shift it by numCells to get correct DAG points (cells go first) */
5070       PetscCall(PetscFindInt(gv, numVerticesAdj, verticesAdj, &lv));
5071       PetscCheck(lv >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Could not find global vertex %" PetscInt_FMT " in local connectivity", gv);
5072       cones[c * numCorners + p] = lv + numCells;
5073     }
5074   }
5075   /* Build point sf */
5076   PetscCall(PetscLayoutCreate(PetscObjectComm((PetscObject)dm), &layout));
5077   PetscCall(PetscLayoutSetSize(layout, NVertices));
5078   PetscCall(PetscLayoutSetLocalSize(layout, numVertices));
5079   PetscCall(PetscLayoutSetBlockSize(layout, 1));
5080   PetscCall(PetscSFCreateByMatchingIndices(layout, numVerticesAdj, verticesAdj, NULL, numCells, numVerticesAdj, verticesAdj, NULL, numCells, vertexSF, &sfPoint));
5081   PetscCall(PetscLayoutDestroy(&layout));
5082   if (!verticesAdjSaved) PetscCall(PetscFree(verticesAdj));
5083   PetscCall(PetscObjectSetName((PetscObject)sfPoint, "point SF"));
5084   if (dm->sf) {
5085     const char *prefix;
5086 
5087     PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm->sf, &prefix));
5088     PetscCall(PetscObjectSetOptionsPrefix((PetscObject)sfPoint, prefix));
5089   }
5090   PetscCall(DMSetPointSF(dm, sfPoint));
5091   PetscCall(PetscSFDestroy(&sfPoint));
5092   if (vertexSF) PetscCall(PetscObjectSetName((PetscObject)*vertexSF, "Vertex Ownership SF"));
5093   /* Fill in the rest of the topology structure */
5094   PetscCall(DMPlexSymmetrize(dm));
5095   PetscCall(DMPlexStratify(dm));
5096   PetscCall(PetscLogEventEnd(DMPLEX_BuildFromCellList, dm, 0, 0, 0));
5097   PetscFunctionReturn(PETSC_SUCCESS);
5098 }
5099 
5100 /*@
5101   DMPlexBuildCoordinatesFromCellListParallel - Build `DM` coordinates from a list of coordinates for each owned vertex (common mesh generator output)
5102 
5103   Collective; No Fortran Support
5104 
5105   Input Parameters:
5106 + dm           - The `DM`
5107 . spaceDim     - The spatial dimension used for coordinates
5108 . sfVert       - `PetscSF` describing complete vertex ownership
5109 - vertexCoords - An array of numVertices*spaceDim numbers, the coordinates of each vertex
5110 
5111   Level: advanced
5112 
5113 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexBuildCoordinatesFromCellList()`, `DMPlexCreateFromCellListParallelPetsc()`, `DMPlexBuildFromCellListParallel()`
5114 @*/
5115 PetscErrorCode DMPlexBuildCoordinatesFromCellListParallel(DM dm, PetscInt spaceDim, PetscSF sfVert, const PetscReal vertexCoords[])
5116 {
5117   PetscSection coordSection;
5118   Vec          coordinates;
5119   PetscScalar *coords;
5120   PetscInt     numVertices, numVerticesAdj, coordSize, v, vStart, vEnd;
5121 
5122   PetscFunctionBegin;
5123   PetscCall(PetscLogEventBegin(DMPLEX_BuildCoordinatesFromCellList, dm, 0, 0, 0));
5124   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
5125   PetscCheck(vStart >= 0 && vEnd >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "DM is not set up properly. DMPlexBuildFromCellList() should be called first.");
5126   PetscCall(DMSetCoordinateDim(dm, spaceDim));
5127   PetscCall(PetscSFGetGraph(sfVert, &numVertices, &numVerticesAdj, NULL, NULL));
5128   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);
5129   PetscCall(DMGetCoordinateSection(dm, &coordSection));
5130   PetscCall(PetscSectionSetNumFields(coordSection, 1));
5131   PetscCall(PetscSectionSetFieldComponents(coordSection, 0, spaceDim));
5132   PetscCall(PetscSectionSetChart(coordSection, vStart, vEnd));
5133   for (v = vStart; v < vEnd; ++v) {
5134     PetscCall(PetscSectionSetDof(coordSection, v, spaceDim));
5135     PetscCall(PetscSectionSetFieldDof(coordSection, v, 0, spaceDim));
5136   }
5137   PetscCall(PetscSectionSetUp(coordSection));
5138   PetscCall(PetscSectionGetStorageSize(coordSection, &coordSize));
5139   PetscCall(VecCreate(PetscObjectComm((PetscObject)dm), &coordinates));
5140   PetscCall(VecSetBlockSize(coordinates, spaceDim));
5141   PetscCall(PetscObjectSetName((PetscObject)coordinates, "coordinates"));
5142   PetscCall(VecSetSizes(coordinates, coordSize, PETSC_DETERMINE));
5143   PetscCall(VecSetType(coordinates, VECSTANDARD));
5144   PetscCall(VecGetArray(coordinates, &coords));
5145   {
5146     MPI_Datatype coordtype;
5147 
5148     /* Need a temp buffer for coords if we have complex/single */
5149     PetscCallMPI(MPI_Type_contiguous(spaceDim, MPIU_SCALAR, &coordtype));
5150     PetscCallMPI(MPI_Type_commit(&coordtype));
5151 #if defined(PETSC_USE_COMPLEX)
5152     {
5153       PetscScalar *svertexCoords;
5154       PetscInt     i;
5155       PetscCall(PetscMalloc1(numVertices * spaceDim, &svertexCoords));
5156       for (i = 0; i < numVertices * spaceDim; i++) svertexCoords[i] = vertexCoords[i];
5157       PetscCall(PetscSFBcastBegin(sfVert, coordtype, svertexCoords, coords, MPI_REPLACE));
5158       PetscCall(PetscSFBcastEnd(sfVert, coordtype, svertexCoords, coords, MPI_REPLACE));
5159       PetscCall(PetscFree(svertexCoords));
5160     }
5161 #else
5162     PetscCall(PetscSFBcastBegin(sfVert, coordtype, vertexCoords, coords, MPI_REPLACE));
5163     PetscCall(PetscSFBcastEnd(sfVert, coordtype, vertexCoords, coords, MPI_REPLACE));
5164 #endif
5165     PetscCallMPI(MPI_Type_free(&coordtype));
5166   }
5167   PetscCall(VecRestoreArray(coordinates, &coords));
5168   PetscCall(DMSetCoordinatesLocal(dm, coordinates));
5169   PetscCall(VecDestroy(&coordinates));
5170   PetscCall(PetscLogEventEnd(DMPLEX_BuildCoordinatesFromCellList, dm, 0, 0, 0));
5171   PetscFunctionReturn(PETSC_SUCCESS);
5172 }
5173 
5174 /*@
5175   DMPlexCreateFromCellListParallelPetsc - Create distributed `DMPLEX` from a list of vertices for each cell (common mesh generator output)
5176 
5177   Collective
5178 
5179   Input Parameters:
5180 + comm         - The communicator
5181 . dim          - The topological dimension of the mesh
5182 . numCells     - The number of cells owned by this process
5183 . numVertices  - The number of vertices owned by this process, or `PETSC_DECIDE`
5184 . NVertices    - The global number of vertices, or `PETSC_DECIDE`
5185 . numCorners   - The number of vertices for each cell
5186 . interpolate  - Flag indicating that intermediate mesh entities (faces, edges) should be created automatically
5187 . cells        - An array of numCells*numCorners numbers, the global vertex numbers for each cell
5188 . spaceDim     - The spatial dimension used for coordinates
5189 - vertexCoords - An array of numVertices*spaceDim numbers, the coordinates of each vertex
5190 
5191   Output Parameters:
5192 + dm          - The `DM`
5193 . vertexSF    - (Optional) `PetscSF` describing complete vertex ownership
5194 - verticesAdj - (Optional) vertex adjacency array
5195 
5196   Level: intermediate
5197 
5198   Notes:
5199   This function is just a convenient sequence of `DMCreate()`, `DMSetType()`, `DMSetDimension()`,
5200   `DMPlexBuildFromCellListParallel()`, `DMPlexInterpolate()`, `DMPlexBuildCoordinatesFromCellListParallel()`
5201 
5202   See `DMPlexBuildFromCellListParallel()` for an example and details about the topology-related parameters.
5203 
5204   See `DMPlexBuildCoordinatesFromCellListParallel()` for details about the geometry-related parameters.
5205 
5206 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateFromCellListPetsc()`, `DMPlexBuildFromCellListParallel()`, `DMPlexBuildCoordinatesFromCellListParallel()`, `DMPlexCreateFromDAG()`, `DMPlexCreate()`
5207 @*/
5208 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)
5209 {
5210   PetscSF sfVert;
5211 
5212   PetscFunctionBegin;
5213   PetscCall(DMCreate(comm, dm));
5214   PetscCall(DMSetType(*dm, DMPLEX));
5215   PetscValidLogicalCollectiveInt(*dm, dim, 2);
5216   PetscValidLogicalCollectiveInt(*dm, spaceDim, 9);
5217   PetscCall(DMSetDimension(*dm, dim));
5218   PetscCall(DMPlexBuildFromCellListParallel(*dm, numCells, numVertices, NVertices, numCorners, cells, &sfVert, verticesAdj));
5219   if (interpolate) {
5220     DM idm;
5221 
5222     PetscCall(DMPlexInterpolate(*dm, &idm));
5223     PetscCall(DMDestroy(dm));
5224     *dm = idm;
5225   }
5226   PetscCall(DMPlexBuildCoordinatesFromCellListParallel(*dm, spaceDim, sfVert, vertexCoords));
5227   if (vertexSF) *vertexSF = sfVert;
5228   else PetscCall(PetscSFDestroy(&sfVert));
5229   PetscFunctionReturn(PETSC_SUCCESS);
5230 }
5231 
5232 /*@
5233   DMPlexBuildFromCellList - Build `DMPLEX` topology from a list of vertices for each cell (common mesh generator output)
5234 
5235   Collective; No Fortran Support
5236 
5237   Input Parameters:
5238 + dm          - The `DM`
5239 . numCells    - The number of cells owned by this process
5240 . numVertices - The number of vertices owned by this process, or `PETSC_DETERMINE`
5241 . numCorners  - The number of vertices for each cell
5242 - cells       - An array of `numCells` x `numCorners` numbers, the global vertex numbers for each cell
5243 
5244   Level: advanced
5245 
5246   Notes:
5247   Two triangles sharing a face
5248 .vb
5249 
5250         2
5251       / | \
5252      /  |  \
5253     /   |   \
5254    0  0 | 1  3
5255     \   |   /
5256      \  |  /
5257       \ | /
5258         1
5259 .ve
5260   would have input
5261 .vb
5262   numCells = 2, numVertices = 4
5263   cells = [0 1 2  1 3 2]
5264 .ve
5265   which would result in the `DMPLEX`
5266 .vb
5267 
5268         4
5269       / | \
5270      /  |  \
5271     /   |   \
5272    2  0 | 1  5
5273     \   |   /
5274      \  |  /
5275       \ | /
5276         3
5277 .ve
5278 
5279   If numVertices is `PETSC_DETERMINE`, it is computed by PETSc as the maximum vertex index in cells + 1.
5280 
5281 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexBuildFromCellListParallel()`, `DMPlexBuildCoordinatesFromCellList()`, `DMPlexCreateFromCellListPetsc()`
5282 @*/
5283 PetscErrorCode DMPlexBuildFromCellList(DM dm, PetscInt numCells, PetscInt numVertices, PetscInt numCorners, const PetscInt cells[])
5284 {
5285   PetscInt *cones, c, p, dim;
5286 
5287   PetscFunctionBegin;
5288   PetscCall(PetscLogEventBegin(DMPLEX_BuildFromCellList, dm, 0, 0, 0));
5289   PetscCall(DMGetDimension(dm, &dim));
5290   /* Get/check global number of vertices */
5291   {
5292     PetscInt       NVerticesInCells, i;
5293     const PetscInt len = numCells * numCorners;
5294 
5295     /* NVerticesInCells = max(cells) + 1 */
5296     NVerticesInCells = PETSC_MIN_INT;
5297     for (i = 0; i < len; i++)
5298       if (cells[i] > NVerticesInCells) NVerticesInCells = cells[i];
5299     ++NVerticesInCells;
5300 
5301     if (numVertices == PETSC_DECIDE) numVertices = NVerticesInCells;
5302     else
5303       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);
5304   }
5305   PetscCall(DMPlexSetChart(dm, 0, numCells + numVertices));
5306   for (c = 0; c < numCells; ++c) PetscCall(DMPlexSetConeSize(dm, c, numCorners));
5307   PetscCall(DMSetUp(dm));
5308   PetscCall(DMPlexGetCones(dm, &cones));
5309   for (c = 0; c < numCells; ++c) {
5310     for (p = 0; p < numCorners; ++p) cones[c * numCorners + p] = cells[c * numCorners + p] + numCells;
5311   }
5312   PetscCall(DMPlexSymmetrize(dm));
5313   PetscCall(DMPlexStratify(dm));
5314   PetscCall(PetscLogEventEnd(DMPLEX_BuildFromCellList, dm, 0, 0, 0));
5315   PetscFunctionReturn(PETSC_SUCCESS);
5316 }
5317 
5318 /*@
5319   DMPlexBuildCoordinatesFromCellList - Build `DM` coordinates from a list of coordinates for each owned vertex (common mesh generator output)
5320 
5321   Collective
5322 
5323   Input Parameters:
5324 + dm           - The `DM`
5325 . spaceDim     - The spatial dimension used for coordinates
5326 - vertexCoords - An array of numVertices*spaceDim numbers, the coordinates of each vertex
5327 
5328   Level: advanced
5329 
5330 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexBuildCoordinatesFromCellListParallel()`, `DMPlexCreateFromCellListPetsc()`, `DMPlexBuildFromCellList()`
5331 @*/
5332 PetscErrorCode DMPlexBuildCoordinatesFromCellList(DM dm, PetscInt spaceDim, const PetscReal vertexCoords[])
5333 {
5334   PetscSection coordSection;
5335   Vec          coordinates;
5336   DM           cdm;
5337   PetscScalar *coords;
5338   PetscInt     v, vStart, vEnd, d;
5339 
5340   PetscFunctionBegin;
5341   PetscCall(PetscLogEventBegin(DMPLEX_BuildCoordinatesFromCellList, dm, 0, 0, 0));
5342   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
5343   PetscCheck(vStart >= 0 && vEnd >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "DM is not set up properly. DMPlexBuildFromCellList() should be called first.");
5344   PetscCall(DMSetCoordinateDim(dm, spaceDim));
5345   PetscCall(DMGetCoordinateSection(dm, &coordSection));
5346   PetscCall(PetscSectionSetNumFields(coordSection, 1));
5347   PetscCall(PetscSectionSetFieldComponents(coordSection, 0, spaceDim));
5348   PetscCall(PetscSectionSetChart(coordSection, vStart, vEnd));
5349   for (v = vStart; v < vEnd; ++v) {
5350     PetscCall(PetscSectionSetDof(coordSection, v, spaceDim));
5351     PetscCall(PetscSectionSetFieldDof(coordSection, v, 0, spaceDim));
5352   }
5353   PetscCall(PetscSectionSetUp(coordSection));
5354 
5355   PetscCall(DMGetCoordinateDM(dm, &cdm));
5356   PetscCall(DMCreateLocalVector(cdm, &coordinates));
5357   PetscCall(VecSetBlockSize(coordinates, spaceDim));
5358   PetscCall(PetscObjectSetName((PetscObject)coordinates, "coordinates"));
5359   PetscCall(VecGetArrayWrite(coordinates, &coords));
5360   for (v = 0; v < vEnd - vStart; ++v) {
5361     for (d = 0; d < spaceDim; ++d) coords[v * spaceDim + d] = vertexCoords[v * spaceDim + d];
5362   }
5363   PetscCall(VecRestoreArrayWrite(coordinates, &coords));
5364   PetscCall(DMSetCoordinatesLocal(dm, coordinates));
5365   PetscCall(VecDestroy(&coordinates));
5366   PetscCall(PetscLogEventEnd(DMPLEX_BuildCoordinatesFromCellList, dm, 0, 0, 0));
5367   PetscFunctionReturn(PETSC_SUCCESS);
5368 }
5369 
5370 /*@
5371   DMPlexCreateFromCellListPetsc - Create `DMPLEX` from a list of vertices for each cell (common mesh generator output), but only process 0 takes in the input
5372 
5373   Collective
5374 
5375   Input Parameters:
5376 + comm         - The communicator
5377 . dim          - The topological dimension of the mesh
5378 . numCells     - The number of cells, only on process 0
5379 . numVertices  - The number of vertices owned by this process, or `PETSC_DECIDE`, only on process 0
5380 . numCorners   - The number of vertices for each cell, only on process 0
5381 . interpolate  - Flag indicating that intermediate mesh entities (faces, edges) should be created automatically
5382 . cells        - An array of numCells*numCorners numbers, the vertices for each cell, only on process 0
5383 . spaceDim     - The spatial dimension used for coordinates
5384 - vertexCoords - An array of numVertices*spaceDim numbers, the coordinates of each vertex, only on process 0
5385 
5386   Output Parameter:
5387 . dm - The `DM`, which only has points on process 0
5388 
5389   Level: intermediate
5390 
5391   Notes:
5392   This function is just a convenient sequence of `DMCreate()`, `DMSetType()`, `DMSetDimension()`, `DMPlexBuildFromCellList()`,
5393   `DMPlexInterpolate()`, `DMPlexBuildCoordinatesFromCellList()`
5394 
5395   See `DMPlexBuildFromCellList()` for an example and details about the topology-related parameters.
5396   See `DMPlexBuildCoordinatesFromCellList()` for details about the geometry-related parameters.
5397   See `DMPlexCreateFromCellListParallelPetsc()` for parallel input
5398 
5399 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateFromCellListParallelPetsc()`, `DMPlexBuildFromCellList()`, `DMPlexBuildCoordinatesFromCellList()`, `DMPlexCreateFromDAG()`, `DMPlexCreate()`
5400 @*/
5401 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)
5402 {
5403   PetscMPIInt rank;
5404 
5405   PetscFunctionBegin;
5406   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.");
5407   PetscCallMPI(MPI_Comm_rank(comm, &rank));
5408   PetscCall(DMCreate(comm, dm));
5409   PetscCall(DMSetType(*dm, DMPLEX));
5410   PetscCall(DMSetDimension(*dm, dim));
5411   if (rank == 0) PetscCall(DMPlexBuildFromCellList(*dm, numCells, numVertices, numCorners, cells));
5412   else PetscCall(DMPlexBuildFromCellList(*dm, 0, 0, 0, NULL));
5413   if (interpolate) {
5414     DM idm;
5415 
5416     PetscCall(DMPlexInterpolate(*dm, &idm));
5417     PetscCall(DMDestroy(dm));
5418     *dm = idm;
5419   }
5420   if (rank == 0) PetscCall(DMPlexBuildCoordinatesFromCellList(*dm, spaceDim, vertexCoords));
5421   else PetscCall(DMPlexBuildCoordinatesFromCellList(*dm, spaceDim, NULL));
5422   PetscFunctionReturn(PETSC_SUCCESS);
5423 }
5424 
5425 /*@
5426   DMPlexCreateFromDAG - This takes as input the adjacency-list representation of the Directed Acyclic Graph (Hasse Diagram) encoding a mesh, and produces a `DM`
5427 
5428   Input Parameters:
5429 + dm               - The empty `DM` object, usually from `DMCreate()` and `DMSetDimension()`
5430 . depth            - The depth of the DAG
5431 . numPoints        - Array of size depth + 1 containing the number of points at each `depth`
5432 . coneSize         - The cone size of each point
5433 . cones            - The concatenation of the cone points for each point, the cone list must be oriented correctly for each point
5434 . coneOrientations - The orientation of each cone point
5435 - vertexCoords     - An array of `numPoints`[0]*spacedim numbers representing the coordinates of each vertex, with spacedim the value set via `DMSetCoordinateDim()`
5436 
5437   Output Parameter:
5438 . dm - The `DM`
5439 
5440   Level: advanced
5441 
5442   Note:
5443   Two triangles sharing a face would have input
5444 .vb
5445   depth = 1, numPoints = [4 2], coneSize = [3 3 0 0 0 0]
5446   cones = [2 3 4  3 5 4], coneOrientations = [0 0 0  0 0 0]
5447  vertexCoords = [-1.0 0.0  0.0 -1.0  0.0 1.0  1.0 0.0]
5448 .ve
5449   which would result in the DMPlex
5450 .vb
5451         4
5452       / | \
5453      /  |  \
5454     /   |   \
5455    2  0 | 1  5
5456     \   |   /
5457      \  |  /
5458       \ | /
5459         3
5460 .ve
5461   Notice that all points are numbered consecutively, unlike `DMPlexCreateFromCellListPetsc()`
5462 
5463 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateFromCellListPetsc()`, `DMPlexCreate()`
5464 @*/
5465 PetscErrorCode DMPlexCreateFromDAG(DM dm, PetscInt depth, const PetscInt numPoints[], const PetscInt coneSize[], const PetscInt cones[], const PetscInt coneOrientations[], const PetscScalar vertexCoords[])
5466 {
5467   Vec          coordinates;
5468   PetscSection coordSection;
5469   PetscScalar *coords;
5470   PetscInt     coordSize, firstVertex = -1, pStart = 0, pEnd = 0, p, v, dim, dimEmbed, d, off;
5471 
5472   PetscFunctionBegin;
5473   PetscCall(DMGetDimension(dm, &dim));
5474   PetscCall(DMGetCoordinateDim(dm, &dimEmbed));
5475   PetscCheck(dimEmbed >= dim, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Embedding dimension %" PetscInt_FMT " cannot be less than intrinsic dimension %" PetscInt_FMT, dimEmbed, dim);
5476   for (d = 0; d <= depth; ++d) pEnd += numPoints[d];
5477   PetscCall(DMPlexSetChart(dm, pStart, pEnd));
5478   for (p = pStart; p < pEnd; ++p) {
5479     PetscCall(DMPlexSetConeSize(dm, p, coneSize[p - pStart]));
5480     if (firstVertex < 0 && !coneSize[p - pStart]) firstVertex = p - pStart;
5481   }
5482   PetscCheck(firstVertex >= 0 || !numPoints[0], PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Expected %" PetscInt_FMT " vertices but could not find any", numPoints[0]);
5483   PetscCall(DMSetUp(dm)); /* Allocate space for cones */
5484   for (p = pStart, off = 0; p < pEnd; off += coneSize[p - pStart], ++p) {
5485     PetscCall(DMPlexSetCone(dm, p, &cones[off]));
5486     PetscCall(DMPlexSetConeOrientation(dm, p, &coneOrientations[off]));
5487   }
5488   PetscCall(DMPlexSymmetrize(dm));
5489   PetscCall(DMPlexStratify(dm));
5490   /* Build coordinates */
5491   PetscCall(DMGetCoordinateSection(dm, &coordSection));
5492   PetscCall(PetscSectionSetNumFields(coordSection, 1));
5493   PetscCall(PetscSectionSetFieldComponents(coordSection, 0, dimEmbed));
5494   PetscCall(PetscSectionSetChart(coordSection, firstVertex, firstVertex + numPoints[0]));
5495   for (v = firstVertex; v < firstVertex + numPoints[0]; ++v) {
5496     PetscCall(PetscSectionSetDof(coordSection, v, dimEmbed));
5497     PetscCall(PetscSectionSetFieldDof(coordSection, v, 0, dimEmbed));
5498   }
5499   PetscCall(PetscSectionSetUp(coordSection));
5500   PetscCall(PetscSectionGetStorageSize(coordSection, &coordSize));
5501   PetscCall(VecCreate(PETSC_COMM_SELF, &coordinates));
5502   PetscCall(PetscObjectSetName((PetscObject)coordinates, "coordinates"));
5503   PetscCall(VecSetSizes(coordinates, coordSize, PETSC_DETERMINE));
5504   PetscCall(VecSetBlockSize(coordinates, dimEmbed));
5505   PetscCall(VecSetType(coordinates, VECSTANDARD));
5506   if (vertexCoords) {
5507     PetscCall(VecGetArray(coordinates, &coords));
5508     for (v = 0; v < numPoints[0]; ++v) {
5509       PetscInt off;
5510 
5511       PetscCall(PetscSectionGetOffset(coordSection, v + firstVertex, &off));
5512       for (d = 0; d < dimEmbed; ++d) coords[off + d] = vertexCoords[v * dimEmbed + d];
5513     }
5514   }
5515   PetscCall(VecRestoreArray(coordinates, &coords));
5516   PetscCall(DMSetCoordinatesLocal(dm, coordinates));
5517   PetscCall(VecDestroy(&coordinates));
5518   PetscFunctionReturn(PETSC_SUCCESS);
5519 }
5520 
5521 /*
5522   DMPlexCreateCellVertexFromFile - Create a `DMPLEX` mesh from a simple cell-vertex file.
5523 
5524   Collective
5525 
5526 + comm        - The MPI communicator
5527 . filename    - Name of the .dat file
5528 - interpolate - Create faces and edges in the mesh
5529 
5530   Output Parameter:
5531 . dm  - The `DM` object representing the mesh
5532 
5533   Level: beginner
5534 
5535   Note:
5536   The format is the simplest possible:
5537 .vb
5538   dim Ne Nv Nc Nl
5539   v_1 v_2 ... v_Nc
5540   ...
5541   x y z marker_1 ... marker_Nl
5542 .ve
5543 
5544   Developer Note:
5545   Should use a `PetscViewer` not a filename
5546 
5547 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateFromFile()`, `DMPlexCreateGmsh()`, `DMPlexCreate()`
5548 */
5549 static PetscErrorCode DMPlexCreateCellVertexFromFile(MPI_Comm comm, const char filename[], PetscBool interpolate, DM *dm)
5550 {
5551   DMLabel      marker;
5552   PetscViewer  viewer;
5553   Vec          coordinates;
5554   PetscSection coordSection;
5555   PetscScalar *coords;
5556   char         line[PETSC_MAX_PATH_LEN];
5557   PetscInt     cdim, coordSize, v, c, d;
5558   PetscMPIInt  rank;
5559   int          snum, dim, Nv, Nc, Ncn, Nl;
5560 
5561   PetscFunctionBegin;
5562   PetscCallMPI(MPI_Comm_rank(comm, &rank));
5563   PetscCall(PetscViewerCreate(comm, &viewer));
5564   PetscCall(PetscViewerSetType(viewer, PETSCVIEWERASCII));
5565   PetscCall(PetscViewerFileSetMode(viewer, FILE_MODE_READ));
5566   PetscCall(PetscViewerFileSetName(viewer, filename));
5567   if (rank == 0) {
5568     PetscCall(PetscViewerRead(viewer, line, 5, NULL, PETSC_STRING));
5569     snum = sscanf(line, "%d %d %d %d %d", &dim, &Nc, &Nv, &Ncn, &Nl);
5570     PetscCheck(snum == 5, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unable to parse cell-vertex file: %s", line);
5571   } else {
5572     Nc = Nv = Ncn = Nl = 0;
5573   }
5574   PetscCallMPI(MPI_Bcast(&dim, 1, MPI_INT, 0, comm));
5575   cdim = (PetscInt)dim;
5576   PetscCall(DMCreate(comm, dm));
5577   PetscCall(DMSetType(*dm, DMPLEX));
5578   PetscCall(DMPlexSetChart(*dm, 0, Nc + Nv));
5579   PetscCall(DMSetDimension(*dm, (PetscInt)dim));
5580   PetscCall(DMSetCoordinateDim(*dm, cdim));
5581   /* Read topology */
5582   if (rank == 0) {
5583     char     format[PETSC_MAX_PATH_LEN];
5584     PetscInt cone[8];
5585     int      vbuf[8], v;
5586 
5587     for (c = 0; c < Ncn; ++c) {
5588       format[c * 3 + 0] = '%';
5589       format[c * 3 + 1] = 'd';
5590       format[c * 3 + 2] = ' ';
5591     }
5592     format[Ncn * 3 - 1] = '\0';
5593     for (c = 0; c < Nc; ++c) PetscCall(DMPlexSetConeSize(*dm, c, Ncn));
5594     PetscCall(DMSetUp(*dm));
5595     for (c = 0; c < Nc; ++c) {
5596       PetscCall(PetscViewerRead(viewer, line, Ncn, NULL, PETSC_STRING));
5597       switch (Ncn) {
5598       case 2:
5599         snum = sscanf(line, format, &vbuf[0], &vbuf[1]);
5600         break;
5601       case 3:
5602         snum = sscanf(line, format, &vbuf[0], &vbuf[1], &vbuf[2]);
5603         break;
5604       case 4:
5605         snum = sscanf(line, format, &vbuf[0], &vbuf[1], &vbuf[2], &vbuf[3]);
5606         break;
5607       case 6:
5608         snum = sscanf(line, format, &vbuf[0], &vbuf[1], &vbuf[2], &vbuf[3], &vbuf[4], &vbuf[5]);
5609         break;
5610       case 8:
5611         snum = sscanf(line, format, &vbuf[0], &vbuf[1], &vbuf[2], &vbuf[3], &vbuf[4], &vbuf[5], &vbuf[6], &vbuf[7]);
5612         break;
5613       default:
5614         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "No cell shape with %d vertices", Ncn);
5615       }
5616       PetscCheck(snum == Ncn, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unable to parse cell-vertex file: %s", line);
5617       for (v = 0; v < Ncn; ++v) cone[v] = vbuf[v] + Nc;
5618       /* Hexahedra are inverted */
5619       if (Ncn == 8) {
5620         PetscInt tmp = cone[1];
5621         cone[1]      = cone[3];
5622         cone[3]      = tmp;
5623       }
5624       PetscCall(DMPlexSetCone(*dm, c, cone));
5625     }
5626   }
5627   PetscCall(DMPlexSymmetrize(*dm));
5628   PetscCall(DMPlexStratify(*dm));
5629   /* Read coordinates */
5630   PetscCall(DMGetCoordinateSection(*dm, &coordSection));
5631   PetscCall(PetscSectionSetNumFields(coordSection, 1));
5632   PetscCall(PetscSectionSetFieldComponents(coordSection, 0, cdim));
5633   PetscCall(PetscSectionSetChart(coordSection, Nc, Nc + Nv));
5634   for (v = Nc; v < Nc + Nv; ++v) {
5635     PetscCall(PetscSectionSetDof(coordSection, v, cdim));
5636     PetscCall(PetscSectionSetFieldDof(coordSection, v, 0, cdim));
5637   }
5638   PetscCall(PetscSectionSetUp(coordSection));
5639   PetscCall(PetscSectionGetStorageSize(coordSection, &coordSize));
5640   PetscCall(VecCreate(PETSC_COMM_SELF, &coordinates));
5641   PetscCall(PetscObjectSetName((PetscObject)coordinates, "coordinates"));
5642   PetscCall(VecSetSizes(coordinates, coordSize, PETSC_DETERMINE));
5643   PetscCall(VecSetBlockSize(coordinates, cdim));
5644   PetscCall(VecSetType(coordinates, VECSTANDARD));
5645   PetscCall(VecGetArray(coordinates, &coords));
5646   if (rank == 0) {
5647     char   format[PETSC_MAX_PATH_LEN];
5648     double x[3];
5649     int    l, val[3];
5650 
5651     if (Nl) {
5652       for (l = 0; l < Nl; ++l) {
5653         format[l * 3 + 0] = '%';
5654         format[l * 3 + 1] = 'd';
5655         format[l * 3 + 2] = ' ';
5656       }
5657       format[Nl * 3 - 1] = '\0';
5658       PetscCall(DMCreateLabel(*dm, "marker"));
5659       PetscCall(DMGetLabel(*dm, "marker", &marker));
5660     }
5661     for (v = 0; v < Nv; ++v) {
5662       PetscCall(PetscViewerRead(viewer, line, 3 + Nl, NULL, PETSC_STRING));
5663       snum = sscanf(line, "%lg %lg %lg", &x[0], &x[1], &x[2]);
5664       PetscCheck(snum == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unable to parse cell-vertex file: %s", line);
5665       switch (Nl) {
5666       case 0:
5667         snum = 0;
5668         break;
5669       case 1:
5670         snum = sscanf(line, format, &val[0]);
5671         break;
5672       case 2:
5673         snum = sscanf(line, format, &val[0], &val[1]);
5674         break;
5675       case 3:
5676         snum = sscanf(line, format, &val[0], &val[1], &val[2]);
5677         break;
5678       default:
5679         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Request support for %d labels", Nl);
5680       }
5681       PetscCheck(snum == Nl, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unable to parse cell-vertex file: %s", line);
5682       for (d = 0; d < cdim; ++d) coords[v * cdim + d] = x[d];
5683       for (l = 0; l < Nl; ++l) PetscCall(DMLabelSetValue(marker, v + Nc, val[l]));
5684     }
5685   }
5686   PetscCall(VecRestoreArray(coordinates, &coords));
5687   PetscCall(DMSetCoordinatesLocal(*dm, coordinates));
5688   PetscCall(VecDestroy(&coordinates));
5689   PetscCall(PetscViewerDestroy(&viewer));
5690   if (interpolate) {
5691     DM      idm;
5692     DMLabel bdlabel;
5693 
5694     PetscCall(DMPlexInterpolate(*dm, &idm));
5695     PetscCall(DMDestroy(dm));
5696     *dm = idm;
5697 
5698     if (!Nl) {
5699       PetscCall(DMCreateLabel(*dm, "marker"));
5700       PetscCall(DMGetLabel(*dm, "marker", &bdlabel));
5701       PetscCall(DMPlexMarkBoundaryFaces(*dm, PETSC_DETERMINE, bdlabel));
5702       PetscCall(DMPlexLabelComplete(*dm, bdlabel));
5703     }
5704   }
5705   PetscFunctionReturn(PETSC_SUCCESS);
5706 }
5707 
5708 /*@
5709   DMPlexCreateFromFile - This takes a filename and produces a `DM`
5710 
5711   Collective
5712 
5713   Input Parameters:
5714 + comm        - The communicator
5715 . filename    - A file name
5716 . plexname    - The object name of the resulting `DM`, also used for intra-datafile lookup by some formats
5717 - interpolate - Flag to create intermediate mesh pieces (edges, faces)
5718 
5719   Output Parameter:
5720 . dm - The `DM`
5721 
5722   Options Database Key:
5723 . -dm_plex_create_from_hdf5_xdmf - use the `PETSC_VIEWER_HDF5_XDMF` format for reading HDF5
5724 
5725   Use `-dm_plex_create_ prefix` to pass options to the internal `PetscViewer`, e.g.
5726 $ -dm_plex_create_viewer_hdf5_collective
5727 
5728   Level: beginner
5729 
5730   Notes:
5731   Using `PETSCVIEWERHDF5` type with `PETSC_VIEWER_HDF5_PETSC` format, one can save multiple `DMPLEX`
5732   meshes in a single HDF5 file. This in turn requires one to name the `DMPLEX` object with `PetscObjectSetName()`
5733   before saving it with `DMView()` and before loading it with `DMLoad()` for identification of the mesh object.
5734   The input parameter name is thus used to name the `DMPLEX` object when `DMPlexCreateFromFile()` internally
5735   calls `DMLoad()`. Currently, name is ignored for other viewer types and/or formats.
5736 
5737 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateFromDAG()`, `DMPlexCreateFromCellListPetsc()`, `DMPlexCreate()`, `PetscObjectSetName()`, `DMView()`, `DMLoad()`
5738 @*/
5739 PetscErrorCode DMPlexCreateFromFile(MPI_Comm comm, const char filename[], const char plexname[], PetscBool interpolate, DM *dm)
5740 {
5741   const char  extGmsh[]      = ".msh";
5742   const char  extGmsh2[]     = ".msh2";
5743   const char  extGmsh4[]     = ".msh4";
5744   const char  extCGNS[]      = ".cgns";
5745   const char  extExodus[]    = ".exo";
5746   const char  extExodus_e[]  = ".e";
5747   const char  extGenesis[]   = ".gen";
5748   const char  extFluent[]    = ".cas";
5749   const char  extHDF5[]      = ".h5";
5750   const char  extXDMFHDF5[]  = ".xdmf.h5";
5751   const char  extPLY[]       = ".ply";
5752   const char  extEGADSLite[] = ".egadslite";
5753   const char  extEGADS[]     = ".egads";
5754   const char  extIGES[]      = ".igs";
5755   const char  extSTEP[]      = ".stp";
5756   const char  extCV[]        = ".dat";
5757   size_t      len;
5758   PetscBool   isGmsh, isGmsh2, isGmsh4, isCGNS, isExodus, isGenesis, isFluent, isHDF5, isPLY, isEGADSLite, isEGADS, isIGES, isSTEP, isCV, isXDMFHDF5;
5759   PetscMPIInt rank;
5760 
5761   PetscFunctionBegin;
5762   PetscAssertPointer(filename, 2);
5763   if (plexname) PetscAssertPointer(plexname, 3);
5764   PetscAssertPointer(dm, 5);
5765   PetscCall(DMInitializePackage());
5766   PetscCall(PetscLogEventBegin(DMPLEX_CreateFromFile, 0, 0, 0, 0));
5767   PetscCallMPI(MPI_Comm_rank(comm, &rank));
5768   PetscCall(PetscStrlen(filename, &len));
5769   PetscCheck(len, comm, PETSC_ERR_ARG_WRONG, "Filename must be a valid path");
5770 
5771 #define CheckExtension(extension__, is_extension__) \
5772   do { \
5773     PetscAssert(sizeof(extension__), comm, PETSC_ERR_PLIB, "Zero-size extension: %s", extension__); \
5774     /* don't count the null-terminator at the end */ \
5775     const size_t ext_len = sizeof(extension__) - 1; \
5776     if (len < ext_len) { \
5777       is_extension__ = PETSC_FALSE; \
5778     } else { \
5779       PetscCall(PetscStrncmp(filename + len - ext_len, extension__, ext_len, &is_extension__)); \
5780     } \
5781   } while (0)
5782 
5783   CheckExtension(extGmsh, isGmsh);
5784   CheckExtension(extGmsh2, isGmsh2);
5785   CheckExtension(extGmsh4, isGmsh4);
5786   CheckExtension(extCGNS, isCGNS);
5787   CheckExtension(extExodus, isExodus);
5788   if (!isExodus) CheckExtension(extExodus_e, isExodus);
5789   CheckExtension(extGenesis, isGenesis);
5790   CheckExtension(extFluent, isFluent);
5791   CheckExtension(extHDF5, isHDF5);
5792   CheckExtension(extPLY, isPLY);
5793   CheckExtension(extEGADSLite, isEGADSLite);
5794   CheckExtension(extEGADS, isEGADS);
5795   CheckExtension(extIGES, isIGES);
5796   CheckExtension(extSTEP, isSTEP);
5797   CheckExtension(extCV, isCV);
5798   CheckExtension(extXDMFHDF5, isXDMFHDF5);
5799 
5800 #undef CheckExtension
5801 
5802   if (isGmsh || isGmsh2 || isGmsh4) {
5803     PetscCall(DMPlexCreateGmshFromFile(comm, filename, interpolate, dm));
5804   } else if (isCGNS) {
5805     PetscCall(DMPlexCreateCGNSFromFile(comm, filename, interpolate, dm));
5806   } else if (isExodus || isGenesis) {
5807     PetscCall(DMPlexCreateExodusFromFile(comm, filename, interpolate, dm));
5808   } else if (isFluent) {
5809     PetscCall(DMPlexCreateFluentFromFile(comm, filename, interpolate, dm));
5810   } else if (isHDF5) {
5811     PetscViewer viewer;
5812 
5813     /* PETSC_VIEWER_HDF5_XDMF is used if the filename ends with .xdmf.h5, or if -dm_plex_create_from_hdf5_xdmf option is present */
5814     PetscCall(PetscOptionsGetBool(NULL, NULL, "-dm_plex_create_from_hdf5_xdmf", &isXDMFHDF5, NULL));
5815     PetscCall(PetscViewerCreate(comm, &viewer));
5816     PetscCall(PetscViewerSetType(viewer, PETSCVIEWERHDF5));
5817     PetscCall(PetscViewerSetOptionsPrefix(viewer, "dm_plex_create_"));
5818     PetscCall(PetscViewerSetFromOptions(viewer));
5819     PetscCall(PetscViewerFileSetMode(viewer, FILE_MODE_READ));
5820     PetscCall(PetscViewerFileSetName(viewer, filename));
5821 
5822     PetscCall(DMCreate(comm, dm));
5823     PetscCall(PetscObjectSetName((PetscObject)*dm, plexname));
5824     PetscCall(DMSetType(*dm, DMPLEX));
5825     if (isXDMFHDF5) PetscCall(PetscViewerPushFormat(viewer, PETSC_VIEWER_HDF5_XDMF));
5826     PetscCall(DMLoad(*dm, viewer));
5827     if (isXDMFHDF5) PetscCall(PetscViewerPopFormat(viewer));
5828     PetscCall(PetscViewerDestroy(&viewer));
5829 
5830     if (interpolate) {
5831       DM idm;
5832 
5833       PetscCall(DMPlexInterpolate(*dm, &idm));
5834       PetscCall(DMDestroy(dm));
5835       *dm = idm;
5836     }
5837   } else if (isPLY) {
5838     PetscCall(DMPlexCreatePLYFromFile(comm, filename, interpolate, dm));
5839   } else if (isEGADSLite || isEGADS || isIGES || isSTEP) {
5840     if (isEGADSLite) PetscCall(DMPlexCreateEGADSLiteFromFile(comm, filename, dm));
5841     else PetscCall(DMPlexCreateEGADSFromFile(comm, filename, dm));
5842     if (!interpolate) {
5843       DM udm;
5844 
5845       PetscCall(DMPlexUninterpolate(*dm, &udm));
5846       PetscCall(DMDestroy(dm));
5847       *dm = udm;
5848     }
5849   } else if (isCV) {
5850     PetscCall(DMPlexCreateCellVertexFromFile(comm, filename, interpolate, dm));
5851   } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cannot load file %s: unrecognized extension", filename);
5852   PetscCall(PetscStrlen(plexname, &len));
5853   if (len) PetscCall(PetscObjectSetName((PetscObject)*dm, plexname));
5854   PetscCall(PetscLogEventEnd(DMPLEX_CreateFromFile, 0, 0, 0, 0));
5855   PetscFunctionReturn(PETSC_SUCCESS);
5856 }
5857 
5858 /*@
5859   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.
5860 
5861   Input Parameters:
5862 + tr     - The `DMPlexTransform`
5863 - prefix - An options prefix, or NULL
5864 
5865   Output Parameter:
5866 . dm - The `DM`
5867 
5868   Level: beginner
5869 
5870   Notes:
5871   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.
5872 
5873 .seealso: `DMPlexCreateFromFile`, `DMPlexCreateFromDAG()`, `DMPlexCreateFromCellListPetsc()`, `DMPlexCreate()`
5874 @*/
5875 PetscErrorCode DMPlexCreateEphemeral(DMPlexTransform tr, const char prefix[], DM *dm)
5876 {
5877   DM           bdm, bcdm, cdm;
5878   Vec          coordinates, coordinatesNew;
5879   PetscSection cs;
5880   PetscInt     dim, cdim, Nl;
5881 
5882   PetscFunctionBegin;
5883   PetscCall(DMCreate(PetscObjectComm((PetscObject)tr), dm));
5884   PetscCall(DMSetType(*dm, DMPLEX));
5885   ((DM_Plex *)(*dm)->data)->interpolated = DMPLEX_INTERPOLATED_FULL;
5886   // Handle coordinates
5887   PetscCall(DMPlexTransformGetDM(tr, &bdm));
5888   PetscCall(DMGetCoordinateDim(bdm, &cdim));
5889   PetscCall(DMSetCoordinateDim(*dm, cdim));
5890   PetscCall(DMGetDimension(bdm, &dim));
5891   PetscCall(DMSetDimension(*dm, dim));
5892   PetscCall(DMGetCoordinateDM(bdm, &bcdm));
5893   PetscCall(DMGetCoordinateDM(*dm, &cdm));
5894   PetscCall(DMCopyDisc(bcdm, cdm));
5895   PetscCall(DMGetLocalSection(cdm, &cs));
5896   PetscCall(PetscSectionSetNumFields(cs, 1));
5897   PetscCall(PetscSectionSetFieldComponents(cs, 0, cdim));
5898   PetscCall(DMGetCoordinatesLocal(bdm, &coordinates));
5899   PetscCall(VecDuplicate(coordinates, &coordinatesNew));
5900   PetscCall(VecCopy(coordinates, coordinatesNew));
5901   PetscCall(DMSetCoordinatesLocal(*dm, coordinatesNew));
5902   PetscCall(VecDestroy(&coordinatesNew));
5903 
5904   PetscCall(PetscObjectReference((PetscObject)tr));
5905   PetscCall(DMPlexTransformDestroy(&((DM_Plex *)(*dm)->data)->tr));
5906   ((DM_Plex *)(*dm)->data)->tr = tr;
5907   PetscCall(DMPlexDistributeSetDefault(*dm, PETSC_FALSE));
5908   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*dm, prefix));
5909   PetscCall(DMSetFromOptions(*dm));
5910 
5911   PetscCall(DMGetNumLabels(bdm, &Nl));
5912   for (PetscInt l = 0; l < Nl; ++l) {
5913     DMLabel     label, labelNew;
5914     const char *lname;
5915     PetscBool   isDepth, isCellType;
5916 
5917     PetscCall(DMGetLabelName(bdm, l, &lname));
5918     PetscCall(PetscStrcmp(lname, "depth", &isDepth));
5919     if (isDepth) continue;
5920     PetscCall(PetscStrcmp(lname, "celltype", &isCellType));
5921     if (isCellType) continue;
5922     PetscCall(DMCreateLabel(*dm, lname));
5923     PetscCall(DMGetLabel(bdm, lname, &label));
5924     PetscCall(DMGetLabel(*dm, lname, &labelNew));
5925     PetscCall(DMLabelSetType(labelNew, DMLABELEPHEMERAL));
5926     PetscCall(DMLabelEphemeralSetLabel(labelNew, label));
5927     PetscCall(DMLabelEphemeralSetTransform(labelNew, tr));
5928     PetscCall(DMLabelSetUp(labelNew));
5929   }
5930   PetscFunctionReturn(PETSC_SUCCESS);
5931 }
5932