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