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