xref: /petsc/src/dm/impls/plex/plexcreate.c (revision ad781fe3223b515d45fb60062f8062326362e786)
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   char    *phases[16];
4921   PetscInt Nphases = 16;
4922   PetscCall(PetscOptionsStringArray("-dm_plex_option_phases", "Option phase prefixes", "DMSetFromOptions", phases, &Nphases, &flg));
4923   PetscOptionsHeadEnd();
4924 
4925   // Phases
4926   if (flg) {
4927     const char *oldPrefix;
4928 
4929     PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &oldPrefix));
4930     for (PetscInt ph = 0; ph < Nphases; ++ph) {
4931       PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)dm, phases[ph]));
4932       PetscCall(PetscInfo(dm, "Options phase %s for DM %s\n", phases[ph], dm->hdr.name));
4933       PetscCall(DMSetFromOptions(dm));
4934       PetscCall(PetscObjectSetOptionsPrefix((PetscObject)dm, oldPrefix));
4935       PetscCall(PetscFree(phases[ph]));
4936     }
4937   }
4938   PetscFunctionReturn(PETSC_SUCCESS);
4939 }
4940 
4941 static PetscErrorCode DMCreateGlobalVector_Plex(DM dm, Vec *vec)
4942 {
4943   PetscFunctionBegin;
4944   PetscCall(DMCreateGlobalVector_Section_Private(dm, vec));
4945   /* PetscCall(VecSetOperation(*vec, VECOP_DUPLICATE, (void(*)(void)) VecDuplicate_MPI_DM)); */
4946   PetscCall(VecSetOperation(*vec, VECOP_VIEW, (void (*)(void))VecView_Plex));
4947   PetscCall(VecSetOperation(*vec, VECOP_VIEWNATIVE, (void (*)(void))VecView_Plex_Native));
4948   PetscCall(VecSetOperation(*vec, VECOP_LOAD, (void (*)(void))VecLoad_Plex));
4949   PetscCall(VecSetOperation(*vec, VECOP_LOADNATIVE, (void (*)(void))VecLoad_Plex_Native));
4950   PetscFunctionReturn(PETSC_SUCCESS);
4951 }
4952 
4953 static PetscErrorCode DMCreateLocalVector_Plex(DM dm, Vec *vec)
4954 {
4955   PetscFunctionBegin;
4956   PetscCall(DMCreateLocalVector_Section_Private(dm, vec));
4957   PetscCall(VecSetOperation(*vec, VECOP_VIEW, (void (*)(void))VecView_Plex_Local));
4958   PetscCall(VecSetOperation(*vec, VECOP_LOAD, (void (*)(void))VecLoad_Plex_Local));
4959   PetscFunctionReturn(PETSC_SUCCESS);
4960 }
4961 
4962 static PetscErrorCode DMGetDimPoints_Plex(DM dm, PetscInt dim, PetscInt *pStart, PetscInt *pEnd)
4963 {
4964   PetscInt depth, d;
4965 
4966   PetscFunctionBegin;
4967   PetscCall(DMPlexGetDepth(dm, &depth));
4968   if (depth == 1) {
4969     PetscCall(DMGetDimension(dm, &d));
4970     if (dim == 0) PetscCall(DMPlexGetDepthStratum(dm, dim, pStart, pEnd));
4971     else if (dim == d) PetscCall(DMPlexGetDepthStratum(dm, 1, pStart, pEnd));
4972     else {
4973       *pStart = 0;
4974       *pEnd   = 0;
4975     }
4976   } else {
4977     PetscCall(DMPlexGetDepthStratum(dm, dim, pStart, pEnd));
4978   }
4979   PetscFunctionReturn(PETSC_SUCCESS);
4980 }
4981 
4982 static PetscErrorCode DMGetNeighbors_Plex(DM dm, PetscInt *nranks, const PetscMPIInt *ranks[])
4983 {
4984   PetscSF            sf;
4985   PetscMPIInt        niranks, njranks;
4986   PetscInt           n;
4987   const PetscMPIInt *iranks, *jranks;
4988   DM_Plex           *data = (DM_Plex *)dm->data;
4989 
4990   PetscFunctionBegin;
4991   PetscCall(DMGetPointSF(dm, &sf));
4992   if (!data->neighbors) {
4993     PetscCall(PetscSFSetUp(sf));
4994     PetscCall(PetscSFGetRootRanks(sf, &njranks, &jranks, NULL, NULL, NULL));
4995     PetscCall(PetscSFGetLeafRanks(sf, &niranks, &iranks, NULL, NULL));
4996     PetscCall(PetscMalloc1(njranks + niranks + 1, &data->neighbors));
4997     PetscCall(PetscArraycpy(data->neighbors + 1, jranks, njranks));
4998     PetscCall(PetscArraycpy(data->neighbors + njranks + 1, iranks, niranks));
4999     n = njranks + niranks;
5000     PetscCall(PetscSortRemoveDupsMPIInt(&n, data->neighbors + 1));
5001     /* The following cast should never fail: can't have more neighbors than PETSC_MPI_INT_MAX */
5002     PetscCall(PetscMPIIntCast(n, data->neighbors));
5003   }
5004   if (nranks) *nranks = data->neighbors[0];
5005   if (ranks) {
5006     if (data->neighbors[0]) *ranks = data->neighbors + 1;
5007     else *ranks = NULL;
5008   }
5009   PetscFunctionReturn(PETSC_SUCCESS);
5010 }
5011 
5012 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM, DM, Mat, Vec, Vec);
5013 
5014 static PetscErrorCode DMInitialize_Plex(DM dm)
5015 {
5016   PetscFunctionBegin;
5017   dm->ops->view                      = DMView_Plex;
5018   dm->ops->load                      = DMLoad_Plex;
5019   dm->ops->setfromoptions            = DMSetFromOptions_Plex;
5020   dm->ops->clone                     = DMClone_Plex;
5021   dm->ops->setup                     = DMSetUp_Plex;
5022   dm->ops->createlocalsection        = DMCreateLocalSection_Plex;
5023   dm->ops->createsectionpermutation  = DMCreateSectionPermutation_Plex;
5024   dm->ops->createdefaultconstraints  = DMCreateDefaultConstraints_Plex;
5025   dm->ops->createglobalvector        = DMCreateGlobalVector_Plex;
5026   dm->ops->createlocalvector         = DMCreateLocalVector_Plex;
5027   dm->ops->getlocaltoglobalmapping   = NULL;
5028   dm->ops->createfieldis             = NULL;
5029   dm->ops->createcoordinatedm        = DMCreateCoordinateDM_Plex;
5030   dm->ops->createcoordinatefield     = DMCreateCoordinateField_Plex;
5031   dm->ops->getcoloring               = NULL;
5032   dm->ops->creatematrix              = DMCreateMatrix_Plex;
5033   dm->ops->createinterpolation       = DMCreateInterpolation_Plex;
5034   dm->ops->createmassmatrix          = DMCreateMassMatrix_Plex;
5035   dm->ops->createmassmatrixlumped    = DMCreateMassMatrixLumped_Plex;
5036   dm->ops->createinjection           = DMCreateInjection_Plex;
5037   dm->ops->refine                    = DMRefine_Plex;
5038   dm->ops->coarsen                   = DMCoarsen_Plex;
5039   dm->ops->refinehierarchy           = DMRefineHierarchy_Plex;
5040   dm->ops->coarsenhierarchy          = DMCoarsenHierarchy_Plex;
5041   dm->ops->extrude                   = DMExtrude_Plex;
5042   dm->ops->globaltolocalbegin        = NULL;
5043   dm->ops->globaltolocalend          = NULL;
5044   dm->ops->localtoglobalbegin        = NULL;
5045   dm->ops->localtoglobalend          = NULL;
5046   dm->ops->destroy                   = DMDestroy_Plex;
5047   dm->ops->createsubdm               = DMCreateSubDM_Plex;
5048   dm->ops->createsuperdm             = DMCreateSuperDM_Plex;
5049   dm->ops->getdimpoints              = DMGetDimPoints_Plex;
5050   dm->ops->locatepoints              = DMLocatePoints_Plex;
5051   dm->ops->projectfunctionlocal      = DMProjectFunctionLocal_Plex;
5052   dm->ops->projectfunctionlabellocal = DMProjectFunctionLabelLocal_Plex;
5053   dm->ops->projectfieldlocal         = DMProjectFieldLocal_Plex;
5054   dm->ops->projectfieldlabellocal    = DMProjectFieldLabelLocal_Plex;
5055   dm->ops->projectbdfieldlabellocal  = DMProjectBdFieldLabelLocal_Plex;
5056   dm->ops->computel2diff             = DMComputeL2Diff_Plex;
5057   dm->ops->computel2gradientdiff     = DMComputeL2GradientDiff_Plex;
5058   dm->ops->computel2fielddiff        = DMComputeL2FieldDiff_Plex;
5059   dm->ops->getneighbors              = DMGetNeighbors_Plex;
5060   dm->ops->getlocalboundingbox       = DMGetLocalBoundingBox_Coordinates;
5061   dm->ops->createdomaindecomposition = DMCreateDomainDecomposition_Plex;
5062   dm->ops->createddscatters          = DMCreateDomainDecompositionScatters_Plex;
5063   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", DMPlexInsertBoundaryValues_Plex));
5064   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerivativeBoundaryValues_C", DMPlexInsertTimeDerivativeBoundaryValues_Plex));
5065   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", DMSetUpGLVisViewer_Plex));
5066   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", DMCreateNeumannOverlap_Plex));
5067   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", DMPlexDistributeGetDefault_Plex));
5068   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", DMPlexDistributeSetDefault_Plex));
5069   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", DMPlexReorderGetDefault_Plex));
5070   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", DMPlexReorderSetDefault_Plex));
5071   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetDefault_C", DMReorderSectionGetDefault_Plex));
5072   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetDefault_C", DMReorderSectionSetDefault_Plex));
5073   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetType_C", DMReorderSectionGetType_Plex));
5074   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetType_C", DMReorderSectionSetType_Plex));
5075   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", DMInterpolateSolution_Plex));
5076   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", DMPlexGetOverlap_Plex));
5077   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", DMPlexSetOverlap_Plex));
5078   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetUseCeed_C", DMPlexGetUseCeed_Plex));
5079   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetUseCeed_C", DMPlexSetUseCeed_Plex));
5080   PetscFunctionReturn(PETSC_SUCCESS);
5081 }
5082 
5083 PETSC_INTERN PetscErrorCode DMClone_Plex(DM dm, DM *newdm)
5084 {
5085   DM_Plex       *mesh = (DM_Plex *)dm->data;
5086   const PetscSF *face_sfs;
5087   PetscInt       num_face_sfs;
5088 
5089   PetscFunctionBegin;
5090   mesh->refct++;
5091   (*newdm)->data = mesh;
5092   PetscCall(DMPlexGetIsoperiodicFaceSF(dm, &num_face_sfs, &face_sfs));
5093   PetscCall(DMPlexSetIsoperiodicFaceSF(*newdm, num_face_sfs, (PetscSF *)face_sfs));
5094   PetscCall(PetscObjectChangeTypeName((PetscObject)*newdm, DMPLEX));
5095   PetscCall(DMInitialize_Plex(*newdm));
5096   PetscFunctionReturn(PETSC_SUCCESS);
5097 }
5098 
5099 /*MC
5100   DMPLEX = "plex" - A `DM` object that encapsulates an unstructured mesh, or CW Complex, which can be expressed using a Hasse Diagram.
5101                     In the local representation, `Vec`s contain all unknowns in the interior and shared boundary. This is
5102                     specified by a PetscSection object. Ownership in the global representation is determined by
5103                     ownership of the underlying `DMPLEX` points. This is specified by another `PetscSection` object.
5104 
5105   Options Database Keys:
5106 + -dm_refine_pre                     - Refine mesh before distribution
5107 + -dm_refine_uniform_pre             - Choose uniform or generator-based refinement
5108 + -dm_refine_volume_limit_pre        - Cell volume limit after pre-refinement using generator
5109 . -dm_distribute                     - Distribute mesh across processes
5110 . -dm_distribute_overlap             - Number of cells to overlap for distribution
5111 . -dm_refine                         - Refine mesh after distribution
5112 . -dm_localize <bool>                - Whether to localize coordinates for periodic meshes
5113 . -dm_sparse_localize <bool>         - Whether to only localize cells on the periodic boundary
5114 . -dm_plex_hash_location             - Use grid hashing for point location
5115 . -dm_plex_hash_box_faces <n,m,p>    - The number of divisions in each direction of the grid hash
5116 . -dm_plex_partition_balance         - Attempt to evenly divide points on partition boundary between processes
5117 . -dm_plex_remesh_bd                 - Allow changes to the boundary on remeshing
5118 . -dm_plex_max_projection_height     - Maximum mesh point height used to project locally
5119 . -dm_plex_regular_refinement        - Use special nested projection algorithm for regular refinement
5120 . -dm_plex_reorder_section           - Use specialized blocking if available
5121 . -dm_plex_check_all                 - Perform all checks below
5122 . -dm_plex_check_symmetry            - Check that the adjacency information in the mesh is symmetric
5123 . -dm_plex_check_skeleton <celltype> - Check that each cell has the correct number of vertices
5124 . -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
5125 . -dm_plex_check_geometry            - Check that cells have positive volume
5126 . -dm_view :mesh.tex:ascii_latex     - View the mesh in LaTeX/TikZ
5127 . -dm_plex_view_scale <num>          - Scale the TikZ
5128 . -dm_plex_print_fem <num>           - View FEM assembly information, such as element vectors and matrices
5129 - -dm_plex_print_fvm <num>           - View FVM assembly information, such as flux updates
5130 
5131   Level: intermediate
5132 
5133 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMType`, `DMPlexCreate()`, `DMCreate()`, `DMSetType()`, `PetscSection`
5134 M*/
5135 
5136 PETSC_EXTERN PetscErrorCode DMCreate_Plex(DM dm)
5137 {
5138   DM_Plex *mesh;
5139   PetscInt unit;
5140 
5141   PetscFunctionBegin;
5142   PetscCall(PetscCitationsRegister(PlexCitation, &Plexcite));
5143   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5144   PetscCall(PetscNew(&mesh));
5145   dm->reorderSection = DM_REORDER_DEFAULT_NOTSET;
5146   dm->data           = mesh;
5147 
5148   mesh->refct = 1;
5149   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &mesh->coneSection));
5150   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &mesh->supportSection));
5151   mesh->refinementUniform      = PETSC_TRUE;
5152   mesh->refinementLimit        = -1.0;
5153   mesh->distDefault            = PETSC_TRUE;
5154   mesh->reorderDefault         = DM_REORDER_DEFAULT_NOTSET;
5155   mesh->distributionName       = NULL;
5156   mesh->interpolated           = DMPLEX_INTERPOLATED_INVALID;
5157   mesh->interpolatedCollective = DMPLEX_INTERPOLATED_INVALID;
5158 
5159   PetscCall(PetscPartitionerCreate(PetscObjectComm((PetscObject)dm), &mesh->partitioner));
5160   mesh->remeshBd = PETSC_FALSE;
5161 
5162   for (unit = 0; unit < NUM_PETSC_UNITS; ++unit) mesh->scale[unit] = 1.0;
5163 
5164   mesh->depthState    = -1;
5165   mesh->celltypeState = -1;
5166   mesh->printTol      = 1.0e-10;
5167   mesh->nonempty_comm = MPI_COMM_SELF;
5168 
5169   PetscCall(DMInitialize_Plex(dm));
5170   PetscFunctionReturn(PETSC_SUCCESS);
5171 }
5172 
5173 /*@
5174   DMPlexCreate - Creates a `DMPLEX` object, which encapsulates an unstructured mesh, or CW complex, which can be expressed using a Hasse Diagram.
5175 
5176   Collective
5177 
5178   Input Parameter:
5179 . comm - The communicator for the `DMPLEX` object
5180 
5181   Output Parameter:
5182 . mesh - The `DMPLEX` object
5183 
5184   Level: beginner
5185 
5186 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMType`, `DMCreate()`, `DMSetType()`
5187 @*/
5188 PetscErrorCode DMPlexCreate(MPI_Comm comm, DM *mesh)
5189 {
5190   PetscFunctionBegin;
5191   PetscAssertPointer(mesh, 2);
5192   PetscCall(DMCreate(comm, mesh));
5193   PetscCall(DMSetType(*mesh, DMPLEX));
5194   PetscFunctionReturn(PETSC_SUCCESS);
5195 }
5196 
5197 /*@C
5198   DMPlexBuildFromCellListParallel - Build distributed `DMPLEX` topology from a list of vertices for each cell (common mesh generator output) where all cells have the same celltype
5199 
5200   Collective; No Fortran Support
5201 
5202   Input Parameters:
5203 + dm          - The `DM`
5204 . numCells    - The number of cells owned by this process
5205 . numVertices - The number of vertices to be owned by this process, or `PETSC_DECIDE`
5206 . NVertices   - The global number of vertices, or `PETSC_DETERMINE`
5207 . numCorners  - The number of vertices for each cell
5208 - cells       - An array of numCells*numCorners numbers, the global vertex numbers for each cell
5209 
5210   Output Parameters:
5211 + vertexSF         - (Optional) `PetscSF` describing complete vertex ownership
5212 - verticesAdjSaved - (Optional) vertex adjacency array
5213 
5214   Level: advanced
5215 
5216   Notes:
5217   Two triangles sharing a face
5218 .vb
5219 
5220         2
5221       / | \
5222      /  |  \
5223     /   |   \
5224    0  0 | 1  3
5225     \   |   /
5226      \  |  /
5227       \ | /
5228         1
5229 .ve
5230   would have input
5231 .vb
5232   numCells = 2, numVertices = 4
5233   cells = [0 1 2  1 3 2]
5234 .ve
5235   which would result in the `DMPLEX`
5236 .vb
5237 
5238         4
5239       / | \
5240      /  |  \
5241     /   |   \
5242    2  0 | 1  5
5243     \   |   /
5244      \  |  /
5245       \ | /
5246         3
5247 .ve
5248 
5249   Vertices are implicitly numbered consecutively 0,...,NVertices.
5250   Each rank owns a chunk of numVertices consecutive vertices.
5251   If numVertices is `PETSC_DECIDE`, PETSc will distribute them as evenly as possible using PetscLayout.
5252   If NVertices is `PETSC_DETERMINE` and numVertices is PETSC_DECIDE, NVertices is computed by PETSc as the maximum vertex index in cells + 1.
5253   If only NVertices is `PETSC_DETERMINE`, it is computed as the sum of numVertices over all ranks.
5254 
5255   The cell distribution is arbitrary non-overlapping, independent of the vertex distribution.
5256 
5257 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexBuildFromCellList()`, `DMPlexCreateFromCellListParallelPetsc()`, `DMPlexBuildCoordinatesFromCellListParallel()`,
5258           `PetscSF`
5259 @*/
5260 PetscErrorCode DMPlexBuildFromCellListParallel(DM dm, PetscInt numCells, PetscInt numVertices, PetscInt NVertices, PetscInt numCorners, const PetscInt cells[], PetscSF *vertexSF, PetscInt **verticesAdjSaved)
5261 {
5262   PetscSF     sfPoint;
5263   PetscLayout layout;
5264   PetscInt    numVerticesAdj, *verticesAdj, *cones, c, p;
5265 
5266   PetscFunctionBegin;
5267   PetscValidLogicalCollectiveInt(dm, NVertices, 4);
5268   PetscCall(PetscLogEventBegin(DMPLEX_BuildFromCellList, dm, 0, 0, 0));
5269   /* Get/check global number of vertices */
5270   {
5271     PetscInt       NVerticesInCells, i;
5272     const PetscInt len = numCells * numCorners;
5273 
5274     /* NVerticesInCells = max(cells) + 1 */
5275     NVerticesInCells = PETSC_INT_MIN;
5276     for (i = 0; i < len; i++)
5277       if (cells[i] > NVerticesInCells) NVerticesInCells = cells[i];
5278     ++NVerticesInCells;
5279     PetscCallMPI(MPIU_Allreduce(MPI_IN_PLACE, &NVerticesInCells, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
5280 
5281     if (numVertices == PETSC_DECIDE && NVertices == PETSC_DECIDE) NVertices = NVerticesInCells;
5282     else
5283       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);
5284   }
5285   /* Count locally unique vertices */
5286   {
5287     PetscHSetI vhash;
5288     PetscInt   off = 0;
5289 
5290     PetscCall(PetscHSetICreate(&vhash));
5291     for (c = 0; c < numCells; ++c) {
5292       for (p = 0; p < numCorners; ++p) PetscCall(PetscHSetIAdd(vhash, cells[c * numCorners + p]));
5293     }
5294     PetscCall(PetscHSetIGetSize(vhash, &numVerticesAdj));
5295     if (!verticesAdjSaved) PetscCall(PetscMalloc1(numVerticesAdj, &verticesAdj));
5296     else verticesAdj = *verticesAdjSaved;
5297     PetscCall(PetscHSetIGetElems(vhash, &off, verticesAdj));
5298     PetscCall(PetscHSetIDestroy(&vhash));
5299     PetscCheck(off == numVerticesAdj, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Invalid number of local vertices %" PetscInt_FMT " should be %" PetscInt_FMT, off, numVerticesAdj);
5300   }
5301   PetscCall(PetscSortInt(numVerticesAdj, verticesAdj));
5302   /* Create cones */
5303   PetscCall(DMPlexSetChart(dm, 0, numCells + numVerticesAdj));
5304   for (c = 0; c < numCells; ++c) PetscCall(DMPlexSetConeSize(dm, c, numCorners));
5305   PetscCall(DMSetUp(dm));
5306   PetscCall(DMPlexGetCones(dm, &cones));
5307   for (c = 0; c < numCells; ++c) {
5308     for (p = 0; p < numCorners; ++p) {
5309       const PetscInt gv = cells[c * numCorners + p];
5310       PetscInt       lv;
5311 
5312       /* Positions within verticesAdj form 0-based local vertex numbering;
5313          we need to shift it by numCells to get correct DAG points (cells go first) */
5314       PetscCall(PetscFindInt(gv, numVerticesAdj, verticesAdj, &lv));
5315       PetscCheck(lv >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Could not find global vertex %" PetscInt_FMT " in local connectivity", gv);
5316       cones[c * numCorners + p] = lv + numCells;
5317     }
5318   }
5319   /* Build point sf */
5320   PetscCall(PetscLayoutCreate(PetscObjectComm((PetscObject)dm), &layout));
5321   PetscCall(PetscLayoutSetSize(layout, NVertices));
5322   PetscCall(PetscLayoutSetLocalSize(layout, numVertices));
5323   PetscCall(PetscLayoutSetBlockSize(layout, 1));
5324   PetscCall(PetscSFCreateByMatchingIndices(layout, numVerticesAdj, verticesAdj, NULL, numCells, numVerticesAdj, verticesAdj, NULL, numCells, vertexSF, &sfPoint));
5325   PetscCall(PetscLayoutDestroy(&layout));
5326   if (!verticesAdjSaved) PetscCall(PetscFree(verticesAdj));
5327   PetscCall(PetscObjectSetName((PetscObject)sfPoint, "point SF"));
5328   if (dm->sf) {
5329     const char *prefix;
5330 
5331     PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm->sf, &prefix));
5332     PetscCall(PetscObjectSetOptionsPrefix((PetscObject)sfPoint, prefix));
5333   }
5334   PetscCall(DMSetPointSF(dm, sfPoint));
5335   PetscCall(PetscSFDestroy(&sfPoint));
5336   if (vertexSF) PetscCall(PetscObjectSetName((PetscObject)*vertexSF, "Vertex Ownership SF"));
5337   /* Fill in the rest of the topology structure */
5338   PetscCall(DMPlexSymmetrize(dm));
5339   PetscCall(DMPlexStratify(dm));
5340   PetscCall(PetscLogEventEnd(DMPLEX_BuildFromCellList, dm, 0, 0, 0));
5341   PetscFunctionReturn(PETSC_SUCCESS);
5342 }
5343 
5344 /*@C
5345   DMPlexBuildFromCellSectionParallel - Build distributed `DMPLEX` topology from a list of vertices for each cell (common mesh generator output) allowing multiple celltypes
5346 
5347   Collective; No Fortran Support
5348 
5349   Input Parameters:
5350 + dm          - The `DM`
5351 . numCells    - The number of cells owned by this process
5352 . numVertices - The number of vertices to be owned by this process, or `PETSC_DECIDE`
5353 . NVertices   - The global number of vertices, or `PETSC_DETERMINE`
5354 . cellSection - The `PetscSection` giving the number of vertices for each cell (layout of cells)
5355 - cells       - An array of the global vertex numbers for each cell
5356 
5357   Output Parameters:
5358 + vertexSF         - (Optional) `PetscSF` describing complete vertex ownership
5359 - verticesAdjSaved - (Optional) vertex adjacency array
5360 
5361   Level: advanced
5362 
5363   Notes:
5364   A triangle and quadrilateral sharing a face
5365 .vb
5366         2----------3
5367       / |          |
5368      /  |          |
5369     /   |          |
5370    0  0 |     1    |
5371     \   |          |
5372      \  |          |
5373       \ |          |
5374         1----------4
5375 .ve
5376   would have input
5377 .vb
5378   numCells = 2, numVertices = 5
5379   cells = [0 1 2  1 4 3 2]
5380 .ve
5381   which would result in the `DMPLEX`
5382 .vb
5383         4----------5
5384       / |          |
5385      /  |          |
5386     /   |          |
5387    2  0 |     1    |
5388     \   |          |
5389      \  |          |
5390       \ |          |
5391         3----------6
5392 .ve
5393 
5394   Vertices are implicitly numbered consecutively 0,...,NVertices.
5395   Each rank owns a chunk of numVertices consecutive vertices.
5396   If numVertices is `PETSC_DECIDE`, PETSc will distribute them as evenly as possible using PetscLayout.
5397   If NVertices is `PETSC_DETERMINE` and numVertices is PETSC_DECIDE, NVertices is computed by PETSc as the maximum vertex index in cells + 1.
5398   If only NVertices is `PETSC_DETERMINE`, it is computed as the sum of numVertices over all ranks.
5399 
5400   The cell distribution is arbitrary non-overlapping, independent of the vertex distribution.
5401 
5402 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexBuildFromCellListParallel()`, `DMPlexCreateFromCellSectionParallel()`, `DMPlexBuildCoordinatesFromCellListParallel()`,
5403           `PetscSF`
5404 @*/
5405 PetscErrorCode DMPlexBuildFromCellSectionParallel(DM dm, PetscInt numCells, PetscInt numVertices, PetscInt NVertices, PetscSection cellSection, const PetscInt cells[], PetscSF *vertexSF, PetscInt **verticesAdjSaved)
5406 {
5407   PetscSF     sfPoint;
5408   PetscLayout layout;
5409   PetscInt    numVerticesAdj, *verticesAdj, *cones, cStart, cEnd, len;
5410 
5411   PetscFunctionBegin;
5412   PetscValidLogicalCollectiveInt(dm, NVertices, 4);
5413   PetscCall(PetscLogEventBegin(DMPLEX_BuildFromCellList, dm, 0, 0, 0));
5414   PetscCall(PetscSectionGetChart(cellSection, &cStart, &cEnd));
5415   PetscCall(PetscSectionGetStorageSize(cellSection, &len));
5416   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);
5417   /* Get/check global number of vertices */
5418   {
5419     PetscInt NVerticesInCells;
5420 
5421     /* NVerticesInCells = max(cells) + 1 */
5422     NVerticesInCells = PETSC_MIN_INT;
5423     for (PetscInt i = 0; i < len; i++)
5424       if (cells[i] > NVerticesInCells) NVerticesInCells = cells[i];
5425     ++NVerticesInCells;
5426     PetscCallMPI(MPIU_Allreduce(MPI_IN_PLACE, &NVerticesInCells, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
5427 
5428     if (numVertices == PETSC_DECIDE && NVertices == PETSC_DECIDE) NVertices = NVerticesInCells;
5429     else
5430       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);
5431   }
5432   /* Count locally unique vertices */
5433   {
5434     PetscHSetI vhash;
5435     PetscInt   off = 0;
5436 
5437     PetscCall(PetscHSetICreate(&vhash));
5438     for (PetscInt i = 0; i < len; i++) PetscCall(PetscHSetIAdd(vhash, cells[i]));
5439     PetscCall(PetscHSetIGetSize(vhash, &numVerticesAdj));
5440     if (!verticesAdjSaved) PetscCall(PetscMalloc1(numVerticesAdj, &verticesAdj));
5441     else verticesAdj = *verticesAdjSaved;
5442     PetscCall(PetscHSetIGetElems(vhash, &off, verticesAdj));
5443     PetscCall(PetscHSetIDestroy(&vhash));
5444     PetscCheck(off == numVerticesAdj, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Invalid number of local vertices %" PetscInt_FMT " should be %" PetscInt_FMT, off, numVerticesAdj);
5445   }
5446   PetscCall(PetscSortInt(numVerticesAdj, verticesAdj));
5447   /* Create cones */
5448   PetscCall(DMPlexSetChart(dm, 0, numCells + numVerticesAdj));
5449   for (PetscInt c = 0; c < numCells; ++c) {
5450     PetscInt dof;
5451 
5452     PetscCall(PetscSectionGetDof(cellSection, c, &dof));
5453     PetscCall(DMPlexSetConeSize(dm, c, dof));
5454   }
5455   PetscCall(DMSetUp(dm));
5456   PetscCall(DMPlexGetCones(dm, &cones));
5457   for (PetscInt c = 0; c < numCells; ++c) {
5458     PetscInt dof, off;
5459 
5460     PetscCall(PetscSectionGetDof(cellSection, c, &dof));
5461     PetscCall(PetscSectionGetOffset(cellSection, c, &off));
5462     for (PetscInt p = off; p < off + dof; ++p) {
5463       const PetscInt gv = cells[p];
5464       PetscInt       lv;
5465 
5466       /* Positions within verticesAdj form 0-based local vertex numbering;
5467          we need to shift it by numCells to get correct DAG points (cells go first) */
5468       PetscCall(PetscFindInt(gv, numVerticesAdj, verticesAdj, &lv));
5469       PetscCheck(lv >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Could not find global vertex %" PetscInt_FMT " in local connectivity", gv);
5470       cones[p] = lv + numCells;
5471     }
5472   }
5473   /* Build point sf */
5474   PetscCall(PetscLayoutCreate(PetscObjectComm((PetscObject)dm), &layout));
5475   PetscCall(PetscLayoutSetSize(layout, NVertices));
5476   PetscCall(PetscLayoutSetLocalSize(layout, numVertices));
5477   PetscCall(PetscLayoutSetBlockSize(layout, 1));
5478   PetscCall(PetscSFCreateByMatchingIndices(layout, numVerticesAdj, verticesAdj, NULL, numCells, numVerticesAdj, verticesAdj, NULL, numCells, vertexSF, &sfPoint));
5479   PetscCall(PetscLayoutDestroy(&layout));
5480   if (!verticesAdjSaved) PetscCall(PetscFree(verticesAdj));
5481   PetscCall(PetscObjectSetName((PetscObject)sfPoint, "point SF"));
5482   if (dm->sf) {
5483     const char *prefix;
5484 
5485     PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm->sf, &prefix));
5486     PetscCall(PetscObjectSetOptionsPrefix((PetscObject)sfPoint, prefix));
5487   }
5488   PetscCall(DMSetPointSF(dm, sfPoint));
5489   PetscCall(PetscSFDestroy(&sfPoint));
5490   if (vertexSF) PetscCall(PetscObjectSetName((PetscObject)*vertexSF, "Vertex Ownership SF"));
5491   /* Fill in the rest of the topology structure */
5492   PetscCall(DMPlexSymmetrize(dm));
5493   PetscCall(DMPlexStratify(dm));
5494   PetscCall(PetscLogEventEnd(DMPLEX_BuildFromCellList, dm, 0, 0, 0));
5495   PetscFunctionReturn(PETSC_SUCCESS);
5496 }
5497 
5498 /*@
5499   DMPlexBuildCoordinatesFromCellListParallel - Build `DM` coordinates from a list of coordinates for each owned vertex (common mesh generator output)
5500 
5501   Collective; No Fortran Support
5502 
5503   Input Parameters:
5504 + dm           - The `DM`
5505 . spaceDim     - The spatial dimension used for coordinates
5506 . sfVert       - `PetscSF` describing complete vertex ownership
5507 - vertexCoords - An array of numVertices*spaceDim numbers, the coordinates of each vertex
5508 
5509   Level: advanced
5510 
5511 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexBuildCoordinatesFromCellList()`, `DMPlexCreateFromCellListParallelPetsc()`, `DMPlexBuildFromCellListParallel()`
5512 @*/
5513 PetscErrorCode DMPlexBuildCoordinatesFromCellListParallel(DM dm, PetscInt spaceDim, PetscSF sfVert, const PetscReal vertexCoords[])
5514 {
5515   PetscSection coordSection;
5516   Vec          coordinates;
5517   PetscScalar *coords;
5518   PetscInt     numVertices, numVerticesAdj, coordSize, v, vStart, vEnd;
5519 
5520   PetscFunctionBegin;
5521   PetscCall(PetscLogEventBegin(DMPLEX_BuildCoordinatesFromCellList, dm, 0, 0, 0));
5522   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
5523   PetscCheck(vStart >= 0 && vEnd >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "DM is not set up properly. DMPlexBuildFromCellList() should be called first.");
5524   PetscCall(DMSetCoordinateDim(dm, spaceDim));
5525   PetscCall(PetscSFGetGraph(sfVert, &numVertices, &numVerticesAdj, NULL, NULL));
5526   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);
5527   PetscCall(DMGetCoordinateSection(dm, &coordSection));
5528   PetscCall(PetscSectionSetNumFields(coordSection, 1));
5529   PetscCall(PetscSectionSetFieldComponents(coordSection, 0, spaceDim));
5530   PetscCall(PetscSectionSetChart(coordSection, vStart, vEnd));
5531   for (v = vStart; v < vEnd; ++v) {
5532     PetscCall(PetscSectionSetDof(coordSection, v, spaceDim));
5533     PetscCall(PetscSectionSetFieldDof(coordSection, v, 0, spaceDim));
5534   }
5535   PetscCall(PetscSectionSetUp(coordSection));
5536   PetscCall(PetscSectionGetStorageSize(coordSection, &coordSize));
5537   PetscCall(VecCreate(PetscObjectComm((PetscObject)dm), &coordinates));
5538   PetscCall(VecSetBlockSize(coordinates, spaceDim));
5539   PetscCall(PetscObjectSetName((PetscObject)coordinates, "coordinates"));
5540   PetscCall(VecSetSizes(coordinates, coordSize, PETSC_DETERMINE));
5541   PetscCall(VecSetType(coordinates, VECSTANDARD));
5542   PetscCall(VecGetArray(coordinates, &coords));
5543   {
5544     MPI_Datatype coordtype;
5545 
5546     /* Need a temp buffer for coords if we have complex/single */
5547     PetscCallMPI(MPI_Type_contiguous((PetscMPIInt)spaceDim, MPIU_SCALAR, &coordtype));
5548     PetscCallMPI(MPI_Type_commit(&coordtype));
5549 #if defined(PETSC_USE_COMPLEX)
5550     {
5551       PetscScalar *svertexCoords;
5552       PetscInt     i;
5553       PetscCall(PetscMalloc1(numVertices * spaceDim, &svertexCoords));
5554       for (i = 0; i < numVertices * spaceDim; i++) svertexCoords[i] = vertexCoords[i];
5555       PetscCall(PetscSFBcastBegin(sfVert, coordtype, svertexCoords, coords, MPI_REPLACE));
5556       PetscCall(PetscSFBcastEnd(sfVert, coordtype, svertexCoords, coords, MPI_REPLACE));
5557       PetscCall(PetscFree(svertexCoords));
5558     }
5559 #else
5560     PetscCall(PetscSFBcastBegin(sfVert, coordtype, vertexCoords, coords, MPI_REPLACE));
5561     PetscCall(PetscSFBcastEnd(sfVert, coordtype, vertexCoords, coords, MPI_REPLACE));
5562 #endif
5563     PetscCallMPI(MPI_Type_free(&coordtype));
5564   }
5565   PetscCall(VecRestoreArray(coordinates, &coords));
5566   PetscCall(DMSetCoordinatesLocal(dm, coordinates));
5567   PetscCall(VecDestroy(&coordinates));
5568   PetscCall(PetscLogEventEnd(DMPLEX_BuildCoordinatesFromCellList, dm, 0, 0, 0));
5569   PetscFunctionReturn(PETSC_SUCCESS);
5570 }
5571 
5572 /*@
5573   DMPlexCreateFromCellListParallelPetsc - Create distributed `DMPLEX` from a list of vertices for each cell (common mesh generator output) where all cells have the same celltype
5574 
5575   Collective
5576 
5577   Input Parameters:
5578 + comm         - The communicator
5579 . dim          - The topological dimension of the mesh
5580 . numCells     - The number of cells owned by this process
5581 . numVertices  - The number of vertices owned by this process, or `PETSC_DECIDE`
5582 . NVertices    - The global number of vertices, or `PETSC_DECIDE`
5583 . numCorners   - The number of vertices for each cell
5584 . interpolate  - Flag indicating that intermediate mesh entities (faces, edges) should be created automatically
5585 . cells        - An array of numCells*numCorners numbers, the global vertex numbers for each cell
5586 . spaceDim     - The spatial dimension used for coordinates
5587 - vertexCoords - An array of numVertices*spaceDim numbers, the coordinates of each vertex
5588 
5589   Output Parameters:
5590 + dm          - The `DM`
5591 . vertexSF    - (Optional) `PetscSF` describing complete vertex ownership
5592 - verticesAdj - (Optional) vertex adjacency array
5593 
5594   Level: intermediate
5595 
5596   Notes:
5597   This function is just a convenient sequence of `DMCreate()`, `DMSetType()`, `DMSetDimension()`,
5598   `DMPlexBuildFromCellListParallel()`, `DMPlexInterpolate()`, `DMPlexBuildCoordinatesFromCellListParallel()`
5599 
5600   See `DMPlexBuildFromCellListParallel()` for an example and details about the topology-related parameters.
5601 
5602   See `DMPlexBuildCoordinatesFromCellListParallel()` for details about the geometry-related parameters.
5603 
5604 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateFromCellListPetsc()`, `DMPlexBuildFromCellListParallel()`, `DMPlexBuildCoordinatesFromCellListParallel()`, `DMPlexCreateFromDAG()`, `DMPlexCreate()`
5605 @*/
5606 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)
5607 {
5608   PetscSF sfVert;
5609 
5610   PetscFunctionBegin;
5611   PetscCall(DMCreate(comm, dm));
5612   PetscCall(DMSetType(*dm, DMPLEX));
5613   PetscValidLogicalCollectiveInt(*dm, dim, 2);
5614   PetscValidLogicalCollectiveInt(*dm, spaceDim, 9);
5615   PetscCall(DMSetDimension(*dm, dim));
5616   PetscCall(DMPlexBuildFromCellListParallel(*dm, numCells, numVertices, NVertices, numCorners, cells, &sfVert, verticesAdj));
5617   if (interpolate) {
5618     DM idm;
5619 
5620     PetscCall(DMPlexInterpolate(*dm, &idm));
5621     PetscCall(DMDestroy(dm));
5622     *dm = idm;
5623   }
5624   PetscCall(DMPlexBuildCoordinatesFromCellListParallel(*dm, spaceDim, sfVert, vertexCoords));
5625   if (vertexSF) *vertexSF = sfVert;
5626   else PetscCall(PetscSFDestroy(&sfVert));
5627   PetscFunctionReturn(PETSC_SUCCESS);
5628 }
5629 
5630 /*@
5631   DMPlexCreateFromCellSectionParallel - Create distributed `DMPLEX` from a list of vertices for each cell (common mesh generator output) and supports multiple celltypes
5632 
5633   Collective
5634 
5635   Input Parameters:
5636 + comm         - The communicator
5637 . dim          - The topological dimension of the mesh
5638 . numCells     - The number of cells owned by this process
5639 . numVertices  - The number of vertices owned by this process, or `PETSC_DECIDE`
5640 . NVertices    - The global number of vertices, or `PETSC_DECIDE`
5641 . cellSection  - The `PetscSection` giving the number of vertices for each cell (layout of cells)
5642 . interpolate  - Flag indicating that intermediate mesh entities (faces, edges) should be created automatically
5643 . cells        - An array of the global vertex numbers for each cell
5644 . spaceDim     - The spatial dimension used for coordinates
5645 - vertexCoords - An array of numVertices*spaceDim numbers, the coordinates of each vertex
5646 
5647   Output Parameters:
5648 + dm          - The `DM`
5649 . vertexSF    - (Optional) `PetscSF` describing complete vertex ownership
5650 - verticesAdj - (Optional) vertex adjacency array
5651 
5652   Level: intermediate
5653 
5654   Notes:
5655   This function is just a convenient sequence of `DMCreate()`, `DMSetType()`, `DMSetDimension()`,
5656   `DMPlexBuildFromCellSectionParallel()`, `DMPlexInterpolate()`, `DMPlexBuildCoordinatesFromCellListParallel()`
5657 
5658   See `DMPlexBuildFromCellSectionParallel()` for an example and details about the topology-related parameters.
5659 
5660   See `DMPlexBuildCoordinatesFromCellListParallel()` for details about the geometry-related parameters.
5661 
5662 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateFromCellListPetsc()`, `DMPlexBuildFromCellListParallel()`, `DMPlexBuildCoordinatesFromCellListParallel()`, `DMPlexCreateFromDAG()`, `DMPlexCreate()`
5663 @*/
5664 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)
5665 {
5666   PetscSF sfVert;
5667 
5668   PetscFunctionBegin;
5669   PetscCall(DMCreate(comm, dm));
5670   PetscCall(DMSetType(*dm, DMPLEX));
5671   PetscValidLogicalCollectiveInt(*dm, dim, 2);
5672   PetscValidLogicalCollectiveInt(*dm, spaceDim, 9);
5673   PetscCall(DMSetDimension(*dm, dim));
5674   PetscCall(DMPlexBuildFromCellSectionParallel(*dm, numCells, numVertices, NVertices, cellSection, cells, &sfVert, verticesAdj));
5675   if (interpolate) {
5676     DM idm;
5677 
5678     PetscCall(DMPlexInterpolate(*dm, &idm));
5679     PetscCall(DMDestroy(dm));
5680     *dm = idm;
5681   }
5682   PetscCall(DMPlexBuildCoordinatesFromCellListParallel(*dm, spaceDim, sfVert, vertexCoords));
5683   if (vertexSF) *vertexSF = sfVert;
5684   else PetscCall(PetscSFDestroy(&sfVert));
5685   PetscFunctionReturn(PETSC_SUCCESS);
5686 }
5687 
5688 /*@
5689   DMPlexBuildFromCellList - Build `DMPLEX` topology from a list of vertices for each cell (common mesh generator output)
5690 
5691   Collective; No Fortran Support
5692 
5693   Input Parameters:
5694 + dm          - The `DM`
5695 . numCells    - The number of cells owned by this process
5696 . numVertices - The number of vertices owned by this process, or `PETSC_DETERMINE`
5697 . numCorners  - The number of vertices for each cell
5698 - cells       - An array of `numCells` x `numCorners` numbers, the global vertex numbers for each cell
5699 
5700   Level: advanced
5701 
5702   Notes:
5703   Two triangles sharing a face
5704 .vb
5705 
5706         2
5707       / | \
5708      /  |  \
5709     /   |   \
5710    0  0 | 1  3
5711     \   |   /
5712      \  |  /
5713       \ | /
5714         1
5715 .ve
5716   would have input
5717 .vb
5718   numCells = 2, numVertices = 4
5719   cells = [0 1 2  1 3 2]
5720 .ve
5721   which would result in the `DMPLEX`
5722 .vb
5723 
5724         4
5725       / | \
5726      /  |  \
5727     /   |   \
5728    2  0 | 1  5
5729     \   |   /
5730      \  |  /
5731       \ | /
5732         3
5733 .ve
5734 
5735   If numVertices is `PETSC_DETERMINE`, it is computed by PETSc as the maximum vertex index in cells + 1.
5736 
5737 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexBuildFromCellListParallel()`, `DMPlexBuildCoordinatesFromCellList()`, `DMPlexCreateFromCellListPetsc()`
5738 @*/
5739 PetscErrorCode DMPlexBuildFromCellList(DM dm, PetscInt numCells, PetscInt numVertices, PetscInt numCorners, const PetscInt cells[])
5740 {
5741   PetscInt *cones, c, p, dim;
5742 
5743   PetscFunctionBegin;
5744   PetscCall(PetscLogEventBegin(DMPLEX_BuildFromCellList, dm, 0, 0, 0));
5745   PetscCall(DMGetDimension(dm, &dim));
5746   /* Get/check global number of vertices */
5747   {
5748     PetscInt       NVerticesInCells, i;
5749     const PetscInt len = numCells * numCorners;
5750 
5751     /* NVerticesInCells = max(cells) + 1 */
5752     NVerticesInCells = PETSC_INT_MIN;
5753     for (i = 0; i < len; i++)
5754       if (cells[i] > NVerticesInCells) NVerticesInCells = cells[i];
5755     ++NVerticesInCells;
5756 
5757     if (numVertices == PETSC_DECIDE) numVertices = NVerticesInCells;
5758     else
5759       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);
5760   }
5761   PetscCall(DMPlexSetChart(dm, 0, numCells + numVertices));
5762   for (c = 0; c < numCells; ++c) PetscCall(DMPlexSetConeSize(dm, c, numCorners));
5763   PetscCall(DMSetUp(dm));
5764   PetscCall(DMPlexGetCones(dm, &cones));
5765   for (c = 0; c < numCells; ++c) {
5766     for (p = 0; p < numCorners; ++p) cones[c * numCorners + p] = cells[c * numCorners + p] + numCells;
5767   }
5768   PetscCall(DMPlexSymmetrize(dm));
5769   PetscCall(DMPlexStratify(dm));
5770   PetscCall(PetscLogEventEnd(DMPLEX_BuildFromCellList, dm, 0, 0, 0));
5771   PetscFunctionReturn(PETSC_SUCCESS);
5772 }
5773 
5774 /*@
5775   DMPlexBuildCoordinatesFromCellList - Build `DM` coordinates from a list of coordinates for each owned vertex (common mesh generator output)
5776 
5777   Collective
5778 
5779   Input Parameters:
5780 + dm           - The `DM`
5781 . spaceDim     - The spatial dimension used for coordinates
5782 - vertexCoords - An array of numVertices*spaceDim numbers, the coordinates of each vertex
5783 
5784   Level: advanced
5785 
5786 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexBuildCoordinatesFromCellListParallel()`, `DMPlexCreateFromCellListPetsc()`, `DMPlexBuildFromCellList()`
5787 @*/
5788 PetscErrorCode DMPlexBuildCoordinatesFromCellList(DM dm, PetscInt spaceDim, const PetscReal vertexCoords[])
5789 {
5790   PetscSection coordSection;
5791   Vec          coordinates;
5792   DM           cdm;
5793   PetscScalar *coords;
5794   PetscInt     v, vStart, vEnd, d;
5795 
5796   PetscFunctionBegin;
5797   PetscCall(PetscLogEventBegin(DMPLEX_BuildCoordinatesFromCellList, dm, 0, 0, 0));
5798   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
5799   PetscCheck(vStart >= 0 && vEnd >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "DM is not set up properly. DMPlexBuildFromCellList() should be called first.");
5800   PetscCall(DMSetCoordinateDim(dm, spaceDim));
5801   PetscCall(DMGetCoordinateSection(dm, &coordSection));
5802   PetscCall(PetscSectionSetNumFields(coordSection, 1));
5803   PetscCall(PetscSectionSetFieldComponents(coordSection, 0, spaceDim));
5804   PetscCall(PetscSectionSetChart(coordSection, vStart, vEnd));
5805   for (v = vStart; v < vEnd; ++v) {
5806     PetscCall(PetscSectionSetDof(coordSection, v, spaceDim));
5807     PetscCall(PetscSectionSetFieldDof(coordSection, v, 0, spaceDim));
5808   }
5809   PetscCall(PetscSectionSetUp(coordSection));
5810 
5811   PetscCall(DMGetCoordinateDM(dm, &cdm));
5812   PetscCall(DMCreateLocalVector(cdm, &coordinates));
5813   PetscCall(VecSetBlockSize(coordinates, spaceDim));
5814   PetscCall(PetscObjectSetName((PetscObject)coordinates, "coordinates"));
5815   PetscCall(VecGetArrayWrite(coordinates, &coords));
5816   for (v = 0; v < vEnd - vStart; ++v) {
5817     for (d = 0; d < spaceDim; ++d) coords[v * spaceDim + d] = vertexCoords[v * spaceDim + d];
5818   }
5819   PetscCall(VecRestoreArrayWrite(coordinates, &coords));
5820   PetscCall(DMSetCoordinatesLocal(dm, coordinates));
5821   PetscCall(VecDestroy(&coordinates));
5822   PetscCall(PetscLogEventEnd(DMPLEX_BuildCoordinatesFromCellList, dm, 0, 0, 0));
5823   PetscFunctionReturn(PETSC_SUCCESS);
5824 }
5825 
5826 /*@
5827   DMPlexCreateFromCellListPetsc - Create `DMPLEX` from a list of vertices for each cell (common mesh generator output), but only process 0 takes in the input
5828 
5829   Collective
5830 
5831   Input Parameters:
5832 + comm         - The communicator
5833 . dim          - The topological dimension of the mesh
5834 . numCells     - The number of cells, only on process 0
5835 . numVertices  - The number of vertices owned by this process, or `PETSC_DECIDE`, only on process 0
5836 . numCorners   - The number of vertices for each cell, only on process 0
5837 . interpolate  - Flag indicating that intermediate mesh entities (faces, edges) should be created automatically
5838 . cells        - An array of numCells*numCorners numbers, the vertices for each cell, only on process 0
5839 . spaceDim     - The spatial dimension used for coordinates
5840 - vertexCoords - An array of numVertices*spaceDim numbers, the coordinates of each vertex, only on process 0
5841 
5842   Output Parameter:
5843 . dm - The `DM`, which only has points on process 0
5844 
5845   Level: intermediate
5846 
5847   Notes:
5848   This function is just a convenient sequence of `DMCreate()`, `DMSetType()`, `DMSetDimension()`, `DMPlexBuildFromCellList()`,
5849   `DMPlexInterpolate()`, `DMPlexBuildCoordinatesFromCellList()`
5850 
5851   See `DMPlexBuildFromCellList()` for an example and details about the topology-related parameters.
5852   See `DMPlexBuildCoordinatesFromCellList()` for details about the geometry-related parameters.
5853   See `DMPlexCreateFromCellListParallelPetsc()` for parallel input
5854 
5855 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateFromCellListParallelPetsc()`, `DMPlexBuildFromCellList()`, `DMPlexBuildCoordinatesFromCellList()`, `DMPlexCreateFromDAG()`, `DMPlexCreate()`
5856 @*/
5857 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)
5858 {
5859   PetscMPIInt rank;
5860 
5861   PetscFunctionBegin;
5862   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.");
5863   PetscCallMPI(MPI_Comm_rank(comm, &rank));
5864   PetscCall(DMCreate(comm, dm));
5865   PetscCall(DMSetType(*dm, DMPLEX));
5866   PetscCall(DMSetDimension(*dm, dim));
5867   if (rank == 0) PetscCall(DMPlexBuildFromCellList(*dm, numCells, numVertices, numCorners, cells));
5868   else PetscCall(DMPlexBuildFromCellList(*dm, 0, 0, 0, NULL));
5869   if (interpolate) {
5870     DM idm;
5871 
5872     PetscCall(DMPlexInterpolate(*dm, &idm));
5873     PetscCall(DMDestroy(dm));
5874     *dm = idm;
5875   }
5876   if (rank == 0) PetscCall(DMPlexBuildCoordinatesFromCellList(*dm, spaceDim, vertexCoords));
5877   else PetscCall(DMPlexBuildCoordinatesFromCellList(*dm, spaceDim, NULL));
5878   PetscFunctionReturn(PETSC_SUCCESS);
5879 }
5880 
5881 /*@
5882   DMPlexCreateFromDAG - This takes as input the adjacency-list representation of the Directed Acyclic Graph (Hasse Diagram) encoding a mesh, and produces a `DM`
5883 
5884   Input Parameters:
5885 + dm               - The empty `DM` object, usually from `DMCreate()` and `DMSetDimension()`
5886 . depth            - The depth of the DAG
5887 . numPoints        - Array of size depth + 1 containing the number of points at each `depth`
5888 . coneSize         - The cone size of each point
5889 . cones            - The concatenation of the cone points for each point, the cone list must be oriented correctly for each point
5890 . coneOrientations - The orientation of each cone point
5891 - vertexCoords     - An array of `numPoints`[0]*spacedim numbers representing the coordinates of each vertex, with spacedim the value set via `DMSetCoordinateDim()`
5892 
5893   Output Parameter:
5894 . dm - The `DM`
5895 
5896   Level: advanced
5897 
5898   Note:
5899   Two triangles sharing a face would have input
5900 .vb
5901   depth = 1, numPoints = [4 2], coneSize = [3 3 0 0 0 0]
5902   cones = [2 3 4  3 5 4], coneOrientations = [0 0 0  0 0 0]
5903  vertexCoords = [-1.0 0.0  0.0 -1.0  0.0 1.0  1.0 0.0]
5904 .ve
5905   which would result in the DMPlex
5906 .vb
5907         4
5908       / | \
5909      /  |  \
5910     /   |   \
5911    2  0 | 1  5
5912     \   |   /
5913      \  |  /
5914       \ | /
5915         3
5916 .ve
5917   Notice that all points are numbered consecutively, unlike `DMPlexCreateFromCellListPetsc()`
5918 
5919 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateFromCellListPetsc()`, `DMPlexCreate()`
5920 @*/
5921 PetscErrorCode DMPlexCreateFromDAG(DM dm, PetscInt depth, const PetscInt numPoints[], const PetscInt coneSize[], const PetscInt cones[], const PetscInt coneOrientations[], const PetscScalar vertexCoords[])
5922 {
5923   Vec          coordinates;
5924   PetscSection coordSection;
5925   PetscScalar *coords;
5926   PetscInt     coordSize, firstVertex = -1, pStart = 0, pEnd = 0, p, v, dim, dimEmbed, d, off;
5927 
5928   PetscFunctionBegin;
5929   PetscCall(DMGetDimension(dm, &dim));
5930   PetscCall(DMGetCoordinateDim(dm, &dimEmbed));
5931   PetscCheck(dimEmbed >= dim, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Embedding dimension %" PetscInt_FMT " cannot be less than intrinsic dimension %" PetscInt_FMT, dimEmbed, dim);
5932   for (d = 0; d <= depth; ++d) pEnd += numPoints[d];
5933   PetscCall(DMPlexSetChart(dm, pStart, pEnd));
5934   for (p = pStart; p < pEnd; ++p) {
5935     PetscCall(DMPlexSetConeSize(dm, p, coneSize[p - pStart]));
5936     if (firstVertex < 0 && !coneSize[p - pStart]) firstVertex = p - pStart;
5937   }
5938   PetscCheck(firstVertex >= 0 || !numPoints[0], PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Expected %" PetscInt_FMT " vertices but could not find any", numPoints[0]);
5939   PetscCall(DMSetUp(dm)); /* Allocate space for cones */
5940   for (p = pStart, off = 0; p < pEnd; off += coneSize[p - pStart], ++p) {
5941     PetscCall(DMPlexSetCone(dm, p, &cones[off]));
5942     PetscCall(DMPlexSetConeOrientation(dm, p, &coneOrientations[off]));
5943   }
5944   PetscCall(DMPlexSymmetrize(dm));
5945   PetscCall(DMPlexStratify(dm));
5946   /* Build coordinates */
5947   PetscCall(DMGetCoordinateSection(dm, &coordSection));
5948   PetscCall(PetscSectionSetNumFields(coordSection, 1));
5949   PetscCall(PetscSectionSetFieldComponents(coordSection, 0, dimEmbed));
5950   PetscCall(PetscSectionSetChart(coordSection, firstVertex, firstVertex + numPoints[0]));
5951   for (v = firstVertex; v < firstVertex + numPoints[0]; ++v) {
5952     PetscCall(PetscSectionSetDof(coordSection, v, dimEmbed));
5953     PetscCall(PetscSectionSetFieldDof(coordSection, v, 0, dimEmbed));
5954   }
5955   PetscCall(PetscSectionSetUp(coordSection));
5956   PetscCall(PetscSectionGetStorageSize(coordSection, &coordSize));
5957   PetscCall(VecCreate(PETSC_COMM_SELF, &coordinates));
5958   PetscCall(PetscObjectSetName((PetscObject)coordinates, "coordinates"));
5959   PetscCall(VecSetSizes(coordinates, coordSize, PETSC_DETERMINE));
5960   PetscCall(VecSetBlockSize(coordinates, dimEmbed));
5961   PetscCall(VecSetType(coordinates, VECSTANDARD));
5962   if (vertexCoords) {
5963     PetscCall(VecGetArray(coordinates, &coords));
5964     for (v = 0; v < numPoints[0]; ++v) {
5965       PetscInt off;
5966 
5967       PetscCall(PetscSectionGetOffset(coordSection, v + firstVertex, &off));
5968       for (d = 0; d < dimEmbed; ++d) coords[off + d] = vertexCoords[v * dimEmbed + d];
5969     }
5970   }
5971   PetscCall(VecRestoreArray(coordinates, &coords));
5972   PetscCall(DMSetCoordinatesLocal(dm, coordinates));
5973   PetscCall(VecDestroy(&coordinates));
5974   PetscFunctionReturn(PETSC_SUCCESS);
5975 }
5976 
5977 /*
5978   DMPlexCreateCellVertexFromFile - Create a `DMPLEX` mesh from a simple cell-vertex file.
5979 
5980   Collective
5981 
5982 + comm        - The MPI communicator
5983 . filename    - Name of the .dat file
5984 - interpolate - Create faces and edges in the mesh
5985 
5986   Output Parameter:
5987 . dm  - The `DM` object representing the mesh
5988 
5989   Level: beginner
5990 
5991   Note:
5992   The format is the simplest possible:
5993 .vb
5994   dim Ne Nv Nc Nl
5995   v_1 v_2 ... v_Nc
5996   ...
5997   x y z marker_1 ... marker_Nl
5998 .ve
5999 
6000   Developer Note:
6001   Should use a `PetscViewer` not a filename
6002 
6003 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateFromFile()`, `DMPlexCreateGmsh()`, `DMPlexCreate()`
6004 */
6005 static PetscErrorCode DMPlexCreateCellVertexFromFile(MPI_Comm comm, const char filename[], PetscBool interpolate, DM *dm)
6006 {
6007   DMLabel      marker;
6008   PetscViewer  viewer;
6009   Vec          coordinates;
6010   PetscSection coordSection;
6011   PetscScalar *coords;
6012   char         line[PETSC_MAX_PATH_LEN];
6013   PetscInt     cdim, coordSize, v, c, d;
6014   PetscMPIInt  rank;
6015   int          snum, dim, Nv, Nc, Ncn, Nl;
6016 
6017   PetscFunctionBegin;
6018   PetscCallMPI(MPI_Comm_rank(comm, &rank));
6019   PetscCall(PetscViewerCreate(comm, &viewer));
6020   PetscCall(PetscViewerSetType(viewer, PETSCVIEWERASCII));
6021   PetscCall(PetscViewerFileSetMode(viewer, FILE_MODE_READ));
6022   PetscCall(PetscViewerFileSetName(viewer, filename));
6023   if (rank == 0) {
6024     PetscCall(PetscViewerRead(viewer, line, 5, NULL, PETSC_STRING));
6025     snum = sscanf(line, "%d %d %d %d %d", &dim, &Nc, &Nv, &Ncn, &Nl);
6026     PetscCheck(snum == 5, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unable to parse cell-vertex file: %s", line);
6027   } else {
6028     Nc = Nv = Ncn = Nl = 0;
6029   }
6030   PetscCallMPI(MPI_Bcast(&dim, 1, MPI_INT, 0, comm));
6031   cdim = (PetscInt)dim;
6032   PetscCall(DMCreate(comm, dm));
6033   PetscCall(DMSetType(*dm, DMPLEX));
6034   PetscCall(DMPlexSetChart(*dm, 0, Nc + Nv));
6035   PetscCall(DMSetDimension(*dm, (PetscInt)dim));
6036   PetscCall(DMSetCoordinateDim(*dm, cdim));
6037   /* Read topology */
6038   if (rank == 0) {
6039     char     format[PETSC_MAX_PATH_LEN];
6040     PetscInt cone[8];
6041     int      vbuf[8], v;
6042 
6043     for (c = 0; c < Ncn; ++c) {
6044       format[c * 3 + 0] = '%';
6045       format[c * 3 + 1] = 'd';
6046       format[c * 3 + 2] = ' ';
6047     }
6048     format[Ncn * 3 - 1] = '\0';
6049     for (c = 0; c < Nc; ++c) PetscCall(DMPlexSetConeSize(*dm, c, Ncn));
6050     PetscCall(DMSetUp(*dm));
6051     for (c = 0; c < Nc; ++c) {
6052       PetscCall(PetscViewerRead(viewer, line, Ncn, NULL, PETSC_STRING));
6053       switch (Ncn) {
6054       case 2:
6055         snum = sscanf(line, format, &vbuf[0], &vbuf[1]);
6056         break;
6057       case 3:
6058         snum = sscanf(line, format, &vbuf[0], &vbuf[1], &vbuf[2]);
6059         break;
6060       case 4:
6061         snum = sscanf(line, format, &vbuf[0], &vbuf[1], &vbuf[2], &vbuf[3]);
6062         break;
6063       case 6:
6064         snum = sscanf(line, format, &vbuf[0], &vbuf[1], &vbuf[2], &vbuf[3], &vbuf[4], &vbuf[5]);
6065         break;
6066       case 8:
6067         snum = sscanf(line, format, &vbuf[0], &vbuf[1], &vbuf[2], &vbuf[3], &vbuf[4], &vbuf[5], &vbuf[6], &vbuf[7]);
6068         break;
6069       default:
6070         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "No cell shape with %d vertices", Ncn);
6071       }
6072       PetscCheck(snum == Ncn, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unable to parse cell-vertex file: %s", line);
6073       for (v = 0; v < Ncn; ++v) cone[v] = vbuf[v] + Nc;
6074       /* Hexahedra are inverted */
6075       if (Ncn == 8) {
6076         PetscInt tmp = cone[1];
6077         cone[1]      = cone[3];
6078         cone[3]      = tmp;
6079       }
6080       PetscCall(DMPlexSetCone(*dm, c, cone));
6081     }
6082   }
6083   PetscCall(DMPlexSymmetrize(*dm));
6084   PetscCall(DMPlexStratify(*dm));
6085   /* Read coordinates */
6086   PetscCall(DMGetCoordinateSection(*dm, &coordSection));
6087   PetscCall(PetscSectionSetNumFields(coordSection, 1));
6088   PetscCall(PetscSectionSetFieldComponents(coordSection, 0, cdim));
6089   PetscCall(PetscSectionSetChart(coordSection, Nc, Nc + Nv));
6090   for (v = Nc; v < Nc + Nv; ++v) {
6091     PetscCall(PetscSectionSetDof(coordSection, v, cdim));
6092     PetscCall(PetscSectionSetFieldDof(coordSection, v, 0, cdim));
6093   }
6094   PetscCall(PetscSectionSetUp(coordSection));
6095   PetscCall(PetscSectionGetStorageSize(coordSection, &coordSize));
6096   PetscCall(VecCreate(PETSC_COMM_SELF, &coordinates));
6097   PetscCall(PetscObjectSetName((PetscObject)coordinates, "coordinates"));
6098   PetscCall(VecSetSizes(coordinates, coordSize, PETSC_DETERMINE));
6099   PetscCall(VecSetBlockSize(coordinates, cdim));
6100   PetscCall(VecSetType(coordinates, VECSTANDARD));
6101   PetscCall(VecGetArray(coordinates, &coords));
6102   if (rank == 0) {
6103     char   format[PETSC_MAX_PATH_LEN];
6104     double x[3];
6105     int    l, val[3];
6106 
6107     if (Nl) {
6108       for (l = 0; l < Nl; ++l) {
6109         format[l * 3 + 0] = '%';
6110         format[l * 3 + 1] = 'd';
6111         format[l * 3 + 2] = ' ';
6112       }
6113       format[Nl * 3 - 1] = '\0';
6114       PetscCall(DMCreateLabel(*dm, "marker"));
6115       PetscCall(DMGetLabel(*dm, "marker", &marker));
6116     }
6117     for (v = 0; v < Nv; ++v) {
6118       PetscCall(PetscViewerRead(viewer, line, 3 + Nl, NULL, PETSC_STRING));
6119       snum = sscanf(line, "%lg %lg %lg", &x[0], &x[1], &x[2]);
6120       PetscCheck(snum == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unable to parse cell-vertex file: %s", line);
6121       switch (Nl) {
6122       case 0:
6123         snum = 0;
6124         break;
6125       case 1:
6126         snum = sscanf(line, format, &val[0]);
6127         break;
6128       case 2:
6129         snum = sscanf(line, format, &val[0], &val[1]);
6130         break;
6131       case 3:
6132         snum = sscanf(line, format, &val[0], &val[1], &val[2]);
6133         break;
6134       default:
6135         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Request support for %d labels", Nl);
6136       }
6137       PetscCheck(snum == Nl, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unable to parse cell-vertex file: %s", line);
6138       for (d = 0; d < cdim; ++d) coords[v * cdim + d] = x[d];
6139       for (l = 0; l < Nl; ++l) PetscCall(DMLabelSetValue(marker, v + Nc, val[l]));
6140     }
6141   }
6142   PetscCall(VecRestoreArray(coordinates, &coords));
6143   PetscCall(DMSetCoordinatesLocal(*dm, coordinates));
6144   PetscCall(VecDestroy(&coordinates));
6145   PetscCall(PetscViewerDestroy(&viewer));
6146   if (interpolate) {
6147     DM      idm;
6148     DMLabel bdlabel;
6149 
6150     PetscCall(DMPlexInterpolate(*dm, &idm));
6151     PetscCall(DMDestroy(dm));
6152     *dm = idm;
6153 
6154     if (!Nl) {
6155       PetscCall(DMCreateLabel(*dm, "marker"));
6156       PetscCall(DMGetLabel(*dm, "marker", &bdlabel));
6157       PetscCall(DMPlexMarkBoundaryFaces(*dm, PETSC_DETERMINE, bdlabel));
6158       PetscCall(DMPlexLabelComplete(*dm, bdlabel));
6159     }
6160   }
6161   PetscFunctionReturn(PETSC_SUCCESS);
6162 }
6163 
6164 /*@
6165   DMPlexCreateFromFile - This takes a filename and produces a `DM`
6166 
6167   Collective
6168 
6169   Input Parameters:
6170 + comm        - The communicator
6171 . filename    - A file name
6172 . plexname    - The object name of the resulting `DM`, also used for intra-datafile lookup by some formats
6173 - interpolate - Flag to create intermediate mesh pieces (edges, faces)
6174 
6175   Output Parameter:
6176 . dm - The `DM`
6177 
6178   Options Database Key:
6179 . -dm_plex_create_from_hdf5_xdmf - use the `PETSC_VIEWER_HDF5_XDMF` format for reading HDF5
6180 
6181   Use `-dm_plex_create_ prefix` to pass options to the internal `PetscViewer`, e.g.
6182 $ -dm_plex_create_viewer_hdf5_collective
6183 
6184   Level: beginner
6185 
6186   Notes:
6187   Using `PETSCVIEWERHDF5` type with `PETSC_VIEWER_HDF5_PETSC` format, one can save multiple `DMPLEX`
6188   meshes in a single HDF5 file. This in turn requires one to name the `DMPLEX` object with `PetscObjectSetName()`
6189   before saving it with `DMView()` and before loading it with `DMLoad()` for identification of the mesh object.
6190   The input parameter name is thus used to name the `DMPLEX` object when `DMPlexCreateFromFile()` internally
6191   calls `DMLoad()`. Currently, name is ignored for other viewer types and/or formats.
6192 
6193 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateFromDAG()`, `DMPlexCreateFromCellListPetsc()`, `DMPlexCreate()`, `PetscObjectSetName()`, `DMView()`, `DMLoad()`
6194 @*/
6195 PetscErrorCode DMPlexCreateFromFile(MPI_Comm comm, const char filename[], const char plexname[], PetscBool interpolate, DM *dm)
6196 {
6197   const char  extGmsh[]      = ".msh";
6198   const char  extGmsh2[]     = ".msh2";
6199   const char  extGmsh4[]     = ".msh4";
6200   const char  extCGNS[]      = ".cgns";
6201   const char  extExodus[]    = ".exo";
6202   const char  extExodus_e[]  = ".e";
6203   const char  extGenesis[]   = ".gen";
6204   const char  extFluent[]    = ".cas";
6205   const char  extHDF5[]      = ".h5";
6206   const char  extXDMFHDF5[]  = ".xdmf.h5";
6207   const char  extPLY[]       = ".ply";
6208   const char  extEGADSLite[] = ".egadslite";
6209   const char  extEGADS[]     = ".egads";
6210   const char  extIGES[]      = ".igs";
6211   const char  extSTEP[]      = ".stp";
6212   const char  extCV[]        = ".dat";
6213   size_t      len;
6214   PetscBool   isGmsh, isGmsh2, isGmsh4, isCGNS, isExodus, isGenesis, isFluent, isHDF5, isPLY, isEGADSLite, isEGADS, isIGES, isSTEP, isCV, isXDMFHDF5;
6215   PetscMPIInt rank;
6216 
6217   PetscFunctionBegin;
6218   PetscAssertPointer(filename, 2);
6219   if (plexname) PetscAssertPointer(plexname, 3);
6220   PetscAssertPointer(dm, 5);
6221   PetscCall(DMInitializePackage());
6222   PetscCall(PetscLogEventBegin(DMPLEX_CreateFromFile, 0, 0, 0, 0));
6223   PetscCallMPI(MPI_Comm_rank(comm, &rank));
6224   PetscCall(PetscStrlen(filename, &len));
6225   PetscCheck(len, comm, PETSC_ERR_ARG_WRONG, "Filename must be a valid path");
6226 
6227 #define CheckExtension(extension__, is_extension__) \
6228   do { \
6229     PetscAssert(sizeof(extension__), comm, PETSC_ERR_PLIB, "Zero-size extension: %s", extension__); \
6230     /* don't count the null-terminator at the end */ \
6231     const size_t ext_len = sizeof(extension__) - 1; \
6232     if (len < ext_len) { \
6233       is_extension__ = PETSC_FALSE; \
6234     } else { \
6235       PetscCall(PetscStrncmp(filename + len - ext_len, extension__, ext_len, &is_extension__)); \
6236     } \
6237   } while (0)
6238 
6239   CheckExtension(extGmsh, isGmsh);
6240   CheckExtension(extGmsh2, isGmsh2);
6241   CheckExtension(extGmsh4, isGmsh4);
6242   CheckExtension(extCGNS, isCGNS);
6243   CheckExtension(extExodus, isExodus);
6244   if (!isExodus) CheckExtension(extExodus_e, isExodus);
6245   CheckExtension(extGenesis, isGenesis);
6246   CheckExtension(extFluent, isFluent);
6247   CheckExtension(extHDF5, isHDF5);
6248   CheckExtension(extPLY, isPLY);
6249   CheckExtension(extEGADSLite, isEGADSLite);
6250   CheckExtension(extEGADS, isEGADS);
6251   CheckExtension(extIGES, isIGES);
6252   CheckExtension(extSTEP, isSTEP);
6253   CheckExtension(extCV, isCV);
6254   CheckExtension(extXDMFHDF5, isXDMFHDF5);
6255 
6256 #undef CheckExtension
6257 
6258   if (isGmsh || isGmsh2 || isGmsh4) {
6259     PetscCall(DMPlexCreateGmshFromFile(comm, filename, interpolate, dm));
6260   } else if (isCGNS) {
6261     PetscCall(DMPlexCreateCGNSFromFile(comm, filename, interpolate, dm));
6262   } else if (isExodus || isGenesis) {
6263     PetscCall(DMPlexCreateExodusFromFile(comm, filename, interpolate, dm));
6264   } else if (isFluent) {
6265     PetscCall(DMPlexCreateFluentFromFile(comm, filename, interpolate, dm));
6266   } else if (isHDF5) {
6267     PetscViewer viewer;
6268 
6269     /* PETSC_VIEWER_HDF5_XDMF is used if the filename ends with .xdmf.h5, or if -dm_plex_create_from_hdf5_xdmf option is present */
6270     PetscCall(PetscOptionsGetBool(NULL, NULL, "-dm_plex_create_from_hdf5_xdmf", &isXDMFHDF5, NULL));
6271     PetscCall(PetscViewerCreate(comm, &viewer));
6272     PetscCall(PetscViewerSetType(viewer, PETSCVIEWERHDF5));
6273     PetscCall(PetscViewerSetOptionsPrefix(viewer, "dm_plex_create_"));
6274     PetscCall(PetscViewerSetFromOptions(viewer));
6275     PetscCall(PetscViewerFileSetMode(viewer, FILE_MODE_READ));
6276     PetscCall(PetscViewerFileSetName(viewer, filename));
6277 
6278     PetscCall(DMCreate(comm, dm));
6279     PetscCall(PetscObjectSetName((PetscObject)*dm, plexname));
6280     PetscCall(DMSetType(*dm, DMPLEX));
6281     if (isXDMFHDF5) PetscCall(PetscViewerPushFormat(viewer, PETSC_VIEWER_HDF5_XDMF));
6282     PetscCall(DMLoad(*dm, viewer));
6283     if (isXDMFHDF5) PetscCall(PetscViewerPopFormat(viewer));
6284     PetscCall(PetscViewerDestroy(&viewer));
6285 
6286     if (interpolate) {
6287       DM idm;
6288 
6289       PetscCall(DMPlexInterpolate(*dm, &idm));
6290       PetscCall(DMDestroy(dm));
6291       *dm = idm;
6292     }
6293   } else if (isPLY) {
6294     PetscCall(DMPlexCreatePLYFromFile(comm, filename, interpolate, dm));
6295   } else if (isEGADSLite || isEGADS || isIGES || isSTEP) {
6296     if (isEGADSLite) PetscCall(DMPlexCreateEGADSLiteFromFile(comm, filename, dm));
6297     else PetscCall(DMPlexCreateEGADSFromFile(comm, filename, dm));
6298     if (!interpolate) {
6299       DM udm;
6300 
6301       PetscCall(DMPlexUninterpolate(*dm, &udm));
6302       PetscCall(DMDestroy(dm));
6303       *dm = udm;
6304     }
6305   } else if (isCV) {
6306     PetscCall(DMPlexCreateCellVertexFromFile(comm, filename, interpolate, dm));
6307   } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cannot load file %s: unrecognized extension", filename);
6308   PetscCall(PetscStrlen(plexname, &len));
6309   if (len) PetscCall(PetscObjectSetName((PetscObject)*dm, plexname));
6310   PetscCall(PetscLogEventEnd(DMPLEX_CreateFromFile, 0, 0, 0, 0));
6311   PetscFunctionReturn(PETSC_SUCCESS);
6312 }
6313 
6314 /*@
6315   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.
6316 
6317   Input Parameters:
6318 + tr     - The `DMPlexTransform`
6319 - prefix - An options prefix, or NULL
6320 
6321   Output Parameter:
6322 . dm - The `DM`
6323 
6324   Level: beginner
6325 
6326   Notes:
6327   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.
6328 
6329 .seealso: `DMPlexCreateFromFile`, `DMPlexCreateFromDAG()`, `DMPlexCreateFromCellListPetsc()`, `DMPlexCreate()`
6330 @*/
6331 PetscErrorCode DMPlexCreateEphemeral(DMPlexTransform tr, const char prefix[], DM *dm)
6332 {
6333   DM           bdm, bcdm, cdm;
6334   Vec          coordinates, coordinatesNew;
6335   PetscSection cs;
6336   PetscInt     cdim, Nl;
6337 
6338   PetscFunctionBegin;
6339   PetscCall(DMCreate(PetscObjectComm((PetscObject)tr), dm));
6340   PetscCall(DMSetType(*dm, DMPLEX));
6341   ((DM_Plex *)(*dm)->data)->interpolated = DMPLEX_INTERPOLATED_FULL;
6342   // Handle coordinates
6343   PetscCall(DMPlexTransformGetDM(tr, &bdm));
6344   PetscCall(DMPlexTransformSetDimensions(tr, bdm, *dm));
6345   PetscCall(DMGetCoordinateDim(*dm, &cdim));
6346   PetscCall(DMGetCoordinateDM(bdm, &bcdm));
6347   PetscCall(DMGetCoordinateDM(*dm, &cdm));
6348   PetscCall(DMCopyDisc(bcdm, cdm));
6349   PetscCall(DMGetLocalSection(cdm, &cs));
6350   PetscCall(PetscSectionSetNumFields(cs, 1));
6351   PetscCall(PetscSectionSetFieldComponents(cs, 0, cdim));
6352   PetscCall(DMGetCoordinatesLocal(bdm, &coordinates));
6353   PetscCall(VecDuplicate(coordinates, &coordinatesNew));
6354   PetscCall(VecCopy(coordinates, coordinatesNew));
6355   PetscCall(DMSetCoordinatesLocal(*dm, coordinatesNew));
6356   PetscCall(VecDestroy(&coordinatesNew));
6357 
6358   PetscCall(PetscObjectReference((PetscObject)tr));
6359   PetscCall(DMPlexTransformDestroy(&((DM_Plex *)(*dm)->data)->tr));
6360   ((DM_Plex *)(*dm)->data)->tr = tr;
6361   PetscCall(DMPlexDistributeSetDefault(*dm, PETSC_FALSE));
6362   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*dm, prefix));
6363   PetscCall(DMSetFromOptions(*dm));
6364 
6365   PetscCall(DMGetNumLabels(bdm, &Nl));
6366   for (PetscInt l = 0; l < Nl; ++l) {
6367     DMLabel     label, labelNew;
6368     const char *lname;
6369     PetscBool   isDepth, isCellType;
6370 
6371     PetscCall(DMGetLabelName(bdm, l, &lname));
6372     PetscCall(PetscStrcmp(lname, "depth", &isDepth));
6373     if (isDepth) continue;
6374     PetscCall(PetscStrcmp(lname, "celltype", &isCellType));
6375     if (isCellType) continue;
6376     PetscCall(DMCreateLabel(*dm, lname));
6377     PetscCall(DMGetLabel(bdm, lname, &label));
6378     PetscCall(DMGetLabel(*dm, lname, &labelNew));
6379     PetscCall(DMLabelSetType(labelNew, DMLABELEPHEMERAL));
6380     PetscCall(DMLabelEphemeralSetLabel(labelNew, label));
6381     PetscCall(DMLabelEphemeralSetTransform(labelNew, tr));
6382     PetscCall(DMLabelSetUp(labelNew));
6383   }
6384   PetscFunctionReturn(PETSC_SUCCESS);
6385 }
6386