xref: /petsc/src/dm/impls/plex/plexcreate.c (revision 8c2ff9f740e90af7294b0fd92a96f07ca768ff24)
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 DestroyExtent_Private(void *extent)
1681 {
1682   return PetscFree(extent);
1683 }
1684 
1685 static PetscErrorCode DMPlexCreateHypercubicMesh_Internal(DM dm, PetscInt dim, const PetscReal lower[], const PetscReal upper[], const PetscInt edges[], const DMBoundaryType bd[])
1686 {
1687   Vec          coordinates;
1688   PetscSection coordSection;
1689   DMLabel      cutLabel    = NULL;
1690   PetscBool    cutMarker   = PETSC_FALSE;
1691   PetscBool    periodic    = PETSC_FALSE;
1692   PetscInt     numCells    = 1, c;
1693   PetscInt     numVertices = 1, v;
1694   PetscScalar *coords;
1695   PetscInt    *vertices, *vert, *vtmp, *supp, cone[2];
1696   PetscInt     d, e, cell = 0, coordSize;
1697   PetscMPIInt  rank;
1698 
1699   PetscFunctionBegin;
1700   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1701   PetscCall(DMSetDimension(dm, dim));
1702   PetscCall(PetscCalloc4(dim, &vertices, dim, &vert, dim, &vtmp, 2 * dim, &supp));
1703   PetscCall(DMCreateLabel(dm, "marker"));
1704   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_periodic_cut", &cutMarker, NULL));
1705   for (d = 0; d < dim; ++d) periodic = (periodic || bd[d] == DM_BOUNDARY_PERIODIC) ? PETSC_TRUE : PETSC_FALSE;
1706   if (periodic && cutMarker) {
1707     PetscCall(DMCreateLabel(dm, "periodic_cut"));
1708     PetscCall(DMGetLabel(dm, "periodic_cut", &cutLabel));
1709   }
1710   for (d = 0; d < dim; ++d) PetscCheck(bd[d] == DM_BOUNDARY_PERIODIC, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Hypercubic mesh must be periodic now");
1711   for (d = 0; d < dim; ++d) {
1712     vertices[d] = edges[d];
1713     numVertices *= vertices[d];
1714   }
1715   numCells = numVertices * dim;
1716   PetscCall(DMPlexSetChart(dm, 0, numCells + numVertices));
1717   for (c = 0; c < numCells; ++c) PetscCall(DMPlexSetConeSize(dm, c, 2));
1718   for (v = numCells; v < numCells + numVertices; ++v) PetscCall(DMPlexSetSupportSize(dm, v, 2 * dim));
1719   /* TODO Loop over boundary and reset support sizes */
1720   PetscCall(DMSetUp(dm)); /* Allocate space for cones and supports */
1721   /* Build cell cones and vertex supports */
1722   PetscCall(DMCreateLabel(dm, "celltype"));
1723   while (vert[dim - 1] < vertices[dim - 1]) {
1724     const PetscInt vertex = TupleToIndex_Private(dim, vertices, vert) + numCells;
1725     PetscInt       s      = 0;
1726 
1727     PetscCall(PetscPrintf(PETSC_COMM_SELF, "Vertex %" PetscInt_FMT ":", vertex));
1728     for (d = 0; d < dim; ++d) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, vert[d]));
1729     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
1730     PetscCall(DMPlexSetCellType(dm, vertex, DM_POLYTOPE_POINT));
1731     for (d = 0; d < dim; ++d) {
1732       for (e = 0; e < dim; ++e) vtmp[e] = vert[e];
1733       vtmp[d] = (vert[d] + 1) % vertices[d];
1734       cone[0] = vertex;
1735       cone[1] = TupleToIndex_Private(dim, vertices, vtmp) + numCells;
1736       PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Vertex %" PetscInt_FMT ":", cone[1]));
1737       for (e = 0; e < dim; ++e) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, vtmp[e]));
1738       PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
1739       PetscCall(DMPlexSetCone(dm, cell, cone));
1740       PetscCall(DMPlexSetCellType(dm, cell, DM_POLYTOPE_SEGMENT));
1741       PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Edge %" PetscInt_FMT " (%" PetscInt_FMT " %" PetscInt_FMT ")\n", cell, cone[0], cone[1]));
1742       ++cell;
1743     }
1744     for (d = 0; d < dim; ++d) {
1745       for (e = 0; e < dim; ++e) vtmp[e] = vert[e];
1746       vtmp[d]   = (vert[d] + vertices[d] - 1) % vertices[d];
1747       supp[s++] = TupleToIndex_Private(dim, vertices, vtmp) * dim + d;
1748       supp[s++] = (vertex - numCells) * dim + d;
1749       PetscCall(DMPlexSetSupport(dm, vertex, supp));
1750     }
1751     PetscCall(DMPlexTensorPointLexicographic_Private(dim, vertices, vert));
1752   }
1753   PetscCall(DMPlexStratify(dm));
1754   /* Build coordinates */
1755   PetscCall(DMGetCoordinateSection(dm, &coordSection));
1756   PetscCall(PetscSectionSetNumFields(coordSection, 1));
1757   PetscCall(PetscSectionSetFieldComponents(coordSection, 0, dim));
1758   PetscCall(PetscSectionSetChart(coordSection, numCells, numCells + numVertices));
1759   for (v = numCells; v < numCells + numVertices; ++v) {
1760     PetscCall(PetscSectionSetDof(coordSection, v, dim));
1761     PetscCall(PetscSectionSetFieldDof(coordSection, v, 0, dim));
1762   }
1763   PetscCall(PetscSectionSetUp(coordSection));
1764   PetscCall(PetscSectionGetStorageSize(coordSection, &coordSize));
1765   PetscCall(VecCreate(PETSC_COMM_SELF, &coordinates));
1766   PetscCall(PetscObjectSetName((PetscObject)coordinates, "coordinates"));
1767   PetscCall(VecSetSizes(coordinates, coordSize, PETSC_DETERMINE));
1768   PetscCall(VecSetBlockSize(coordinates, dim));
1769   PetscCall(VecSetType(coordinates, VECSTANDARD));
1770   PetscCall(VecGetArray(coordinates, &coords));
1771   for (d = 0; d < dim; ++d) vert[d] = 0;
1772   while (vert[dim - 1] < vertices[dim - 1]) {
1773     const PetscInt vertex = TupleToIndex_Private(dim, vertices, vert);
1774 
1775     for (d = 0; d < dim; ++d) coords[vertex * dim + d] = lower[d] + ((upper[d] - lower[d]) / vertices[d]) * vert[d];
1776     PetscCall(PetscPrintf(PETSC_COMM_SELF, "Vertex %" PetscInt_FMT ":", vertex));
1777     for (d = 0; d < dim; ++d) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, vert[d]));
1778     for (d = 0; d < dim; ++d) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %g", (double)PetscRealPart(coords[vertex * dim + d])));
1779     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
1780     PetscCall(DMPlexTensorPointLexicographic_Private(dim, vertices, vert));
1781   }
1782   PetscCall(VecRestoreArray(coordinates, &coords));
1783   PetscCall(DMSetCoordinatesLocal(dm, coordinates));
1784   PetscCall(VecDestroy(&coordinates));
1785   PetscCall(PetscFree4(vertices, vert, vtmp, supp));
1786   //PetscCall(DMSetPeriodicity(dm, NULL, lower, upper));
1787   // Attach the extent
1788   {
1789     PetscContainer c;
1790     PetscInt      *extent;
1791 
1792     PetscCall(PetscMalloc1(dim, &extent));
1793     for (PetscInt d = 0; d < dim; ++d) extent[d] = edges[d];
1794     PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &c));
1795     PetscCall(PetscContainerSetUserDestroy(c, DestroyExtent_Private));
1796     PetscCall(PetscContainerSetPointer(c, extent));
1797     PetscCall(PetscObjectCompose((PetscObject)dm, "_extent", (PetscObject)c));
1798     PetscCall(PetscContainerDestroy(&c));
1799   }
1800   PetscFunctionReturn(PETSC_SUCCESS);
1801 }
1802 
1803 /*@C
1804   DMPlexCreateHypercubicMesh - Creates a periodic mesh on the tensor product of unit intervals using only vertices and edges.
1805 
1806   Collective
1807 
1808   Input Parameters:
1809 + comm  - The communicator for the DM object
1810 . dim   - The spatial dimension
1811 . edges - Number of edges per dimension, or `NULL` for (1,) in 1D and (2, 2) in 2D and (1, 1, 1) in 3D
1812 . lower - The lower left corner, or `NULL` for (0, 0, 0)
1813 - upper - The upper right corner, or `NULL` for (1, 1, 1)
1814 
1815   Output Parameter:
1816 . dm - The DM object
1817 
1818   Level: beginner
1819 
1820   Note:
1821   If you want to customize this mesh using options, you just need to
1822 .vb
1823   DMCreate(comm, &dm);
1824   DMSetType(dm, DMPLEX);
1825   DMSetFromOptions(dm);
1826 .ve
1827   and use the options on the `DMSetFromOptions()` page.
1828 
1829   The vertices are numbered is lexicographic order, and the dim edges exiting a vertex in the positive orthant are number consecutively,
1830 .vb
1831  18--0-19--2-20--4-18
1832   |     |     |     |
1833  13    15    17    13
1834   |     |     |     |
1835  24-12-25-14-26-16-24
1836   |     |     |     |
1837   7     9    11     7
1838   |     |     |     |
1839  21--6-22--8-23-10-21
1840   |     |     |     |
1841   1     3     5     1
1842   |     |     |     |
1843  18--0-19--2-20--4-18
1844 .ve
1845 
1846 .seealso: `DMSetFromOptions()`, `DMPlexCreateFromFile()`, `DMPlexCreateHexCylinderMesh()`, `DMSetType()`, `DMCreate()`
1847 @*/
1848 PetscErrorCode DMPlexCreateHypercubicMesh(MPI_Comm comm, PetscInt dim, const PetscInt edges[], const PetscReal lower[], const PetscReal upper[], DM *dm)
1849 {
1850   PetscInt       *edg;
1851   PetscReal      *low, *upp;
1852   DMBoundaryType *bdt;
1853   PetscInt        d;
1854 
1855   PetscFunctionBegin;
1856   PetscCall(DMCreate(comm, dm));
1857   PetscCall(DMSetType(*dm, DMPLEX));
1858   PetscCall(PetscMalloc4(dim, &edg, dim, &low, dim, &upp, dim, &bdt));
1859   for (d = 0; d < dim; ++d) {
1860     edg[d] = edges ? edges[d] : 1;
1861     low[d] = lower ? lower[d] : 0.;
1862     upp[d] = upper ? upper[d] : 1.;
1863     bdt[d] = DM_BOUNDARY_PERIODIC;
1864   }
1865   PetscCall(DMPlexCreateHypercubicMesh_Internal(*dm, dim, low, upp, edg, bdt));
1866   PetscCall(PetscFree4(edg, low, upp, bdt));
1867   PetscFunctionReturn(PETSC_SUCCESS);
1868 }
1869 
1870 /*@
1871   DMPlexSetOptionsPrefix - Sets the prefix used for searching for all `DM` options in the database.
1872 
1873   Logically Collective
1874 
1875   Input Parameters:
1876 + dm     - the `DM` context
1877 - prefix - the prefix to prepend to all option names
1878 
1879   Level: advanced
1880 
1881   Note:
1882   A hyphen (-) must NOT be given at the beginning of the prefix name.
1883   The first character of all runtime options is AUTOMATICALLY the hyphen.
1884 
1885 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `SNESSetFromOptions()`
1886 @*/
1887 PetscErrorCode DMPlexSetOptionsPrefix(DM dm, const char prefix[])
1888 {
1889   DM_Plex *mesh = (DM_Plex *)dm->data;
1890 
1891   PetscFunctionBegin;
1892   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1893   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)dm, prefix));
1894   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)mesh->partitioner, prefix));
1895   PetscFunctionReturn(PETSC_SUCCESS);
1896 }
1897 
1898 /* Remap geometry to cylinder
1899    TODO: This only works for a single refinement, then it is broken
1900 
1901      Interior square: Linear interpolation is correct
1902      The other cells all have vertices on rays from the origin. We want to uniformly expand the spacing
1903      such that the last vertex is on the unit circle. So the closest and farthest vertices are at distance
1904 
1905        phi     = arctan(y/x)
1906        d_close = sqrt(1/8 + 1/4 sin^2(phi))
1907        d_far   = sqrt(1/2 + sin^2(phi))
1908 
1909      so we remap them using
1910 
1911        x_new = x_close + (x - x_close) (1 - d_close) / (d_far - d_close)
1912        y_new = y_close + (y - y_close) (1 - d_close) / (d_far - d_close)
1913 
1914      If pi/4 < phi < 3pi/4 or -3pi/4 < phi < -pi/4, then we switch x and y.
1915 */
1916 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[])
1917 {
1918   const PetscReal dis = 1.0 / PetscSqrtReal(2.0);
1919   const PetscReal ds2 = 0.5 * dis;
1920 
1921   if ((PetscAbsScalar(u[0]) <= ds2) && (PetscAbsScalar(u[1]) <= ds2)) {
1922     f0[0] = u[0];
1923     f0[1] = u[1];
1924   } else {
1925     PetscReal phi, sinp, cosp, dc, df, x, y, xc, yc;
1926 
1927     x    = PetscRealPart(u[0]);
1928     y    = PetscRealPart(u[1]);
1929     phi  = PetscAtan2Real(y, x);
1930     sinp = PetscSinReal(phi);
1931     cosp = PetscCosReal(phi);
1932     if ((PetscAbsReal(phi) > PETSC_PI / 4.0) && (PetscAbsReal(phi) < 3.0 * PETSC_PI / 4.0)) {
1933       dc = PetscAbsReal(ds2 / sinp);
1934       df = PetscAbsReal(dis / sinp);
1935       xc = ds2 * x / PetscAbsReal(y);
1936       yc = ds2 * PetscSignReal(y);
1937     } else {
1938       dc = PetscAbsReal(ds2 / cosp);
1939       df = PetscAbsReal(dis / cosp);
1940       xc = ds2 * PetscSignReal(x);
1941       yc = ds2 * y / PetscAbsReal(x);
1942     }
1943     f0[0] = xc + (u[0] - xc) * (1.0 - dc) / (df - dc);
1944     f0[1] = yc + (u[1] - yc) * (1.0 - dc) / (df - dc);
1945   }
1946   f0[2] = u[2];
1947 }
1948 
1949 static PetscErrorCode DMPlexCreateHexCylinderMesh_Internal(DM dm, DMBoundaryType periodicZ, PetscInt Nr)
1950 {
1951   const PetscInt dim = 3;
1952   PetscInt       numCells, numVertices;
1953   PetscMPIInt    rank;
1954 
1955   PetscFunctionBegin;
1956   PetscCall(PetscLogEventBegin(DMPLEX_Generate, dm, 0, 0, 0));
1957   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1958   PetscCall(DMSetDimension(dm, dim));
1959   /* Create topology */
1960   {
1961     PetscInt cone[8], c;
1962 
1963     numCells    = rank == 0 ? 5 : 0;
1964     numVertices = rank == 0 ? 16 : 0;
1965     if (periodicZ == DM_BOUNDARY_PERIODIC) {
1966       numCells *= 3;
1967       numVertices = rank == 0 ? 24 : 0;
1968     }
1969     PetscCall(DMPlexSetChart(dm, 0, numCells + numVertices));
1970     for (c = 0; c < numCells; c++) PetscCall(DMPlexSetConeSize(dm, c, 8));
1971     PetscCall(DMSetUp(dm));
1972     if (rank == 0) {
1973       if (periodicZ == DM_BOUNDARY_PERIODIC) {
1974         cone[0] = 15;
1975         cone[1] = 18;
1976         cone[2] = 17;
1977         cone[3] = 16;
1978         cone[4] = 31;
1979         cone[5] = 32;
1980         cone[6] = 33;
1981         cone[7] = 34;
1982         PetscCall(DMPlexSetCone(dm, 0, cone));
1983         cone[0] = 16;
1984         cone[1] = 17;
1985         cone[2] = 24;
1986         cone[3] = 23;
1987         cone[4] = 32;
1988         cone[5] = 36;
1989         cone[6] = 37;
1990         cone[7] = 33; /* 22 25 26 21 */
1991         PetscCall(DMPlexSetCone(dm, 1, cone));
1992         cone[0] = 18;
1993         cone[1] = 27;
1994         cone[2] = 24;
1995         cone[3] = 17;
1996         cone[4] = 34;
1997         cone[5] = 33;
1998         cone[6] = 37;
1999         cone[7] = 38;
2000         PetscCall(DMPlexSetCone(dm, 2, cone));
2001         cone[0] = 29;
2002         cone[1] = 27;
2003         cone[2] = 18;
2004         cone[3] = 15;
2005         cone[4] = 35;
2006         cone[5] = 31;
2007         cone[6] = 34;
2008         cone[7] = 38;
2009         PetscCall(DMPlexSetCone(dm, 3, cone));
2010         cone[0] = 29;
2011         cone[1] = 15;
2012         cone[2] = 16;
2013         cone[3] = 23;
2014         cone[4] = 35;
2015         cone[5] = 36;
2016         cone[6] = 32;
2017         cone[7] = 31;
2018         PetscCall(DMPlexSetCone(dm, 4, cone));
2019 
2020         cone[0] = 31;
2021         cone[1] = 34;
2022         cone[2] = 33;
2023         cone[3] = 32;
2024         cone[4] = 19;
2025         cone[5] = 22;
2026         cone[6] = 21;
2027         cone[7] = 20;
2028         PetscCall(DMPlexSetCone(dm, 5, cone));
2029         cone[0] = 32;
2030         cone[1] = 33;
2031         cone[2] = 37;
2032         cone[3] = 36;
2033         cone[4] = 22;
2034         cone[5] = 25;
2035         cone[6] = 26;
2036         cone[7] = 21;
2037         PetscCall(DMPlexSetCone(dm, 6, cone));
2038         cone[0] = 34;
2039         cone[1] = 38;
2040         cone[2] = 37;
2041         cone[3] = 33;
2042         cone[4] = 20;
2043         cone[5] = 21;
2044         cone[6] = 26;
2045         cone[7] = 28;
2046         PetscCall(DMPlexSetCone(dm, 7, cone));
2047         cone[0] = 35;
2048         cone[1] = 38;
2049         cone[2] = 34;
2050         cone[3] = 31;
2051         cone[4] = 30;
2052         cone[5] = 19;
2053         cone[6] = 20;
2054         cone[7] = 28;
2055         PetscCall(DMPlexSetCone(dm, 8, cone));
2056         cone[0] = 35;
2057         cone[1] = 31;
2058         cone[2] = 32;
2059         cone[3] = 36;
2060         cone[4] = 30;
2061         cone[5] = 25;
2062         cone[6] = 22;
2063         cone[7] = 19;
2064         PetscCall(DMPlexSetCone(dm, 9, cone));
2065 
2066         cone[0] = 19;
2067         cone[1] = 20;
2068         cone[2] = 21;
2069         cone[3] = 22;
2070         cone[4] = 15;
2071         cone[5] = 16;
2072         cone[6] = 17;
2073         cone[7] = 18;
2074         PetscCall(DMPlexSetCone(dm, 10, cone));
2075         cone[0] = 22;
2076         cone[1] = 21;
2077         cone[2] = 26;
2078         cone[3] = 25;
2079         cone[4] = 16;
2080         cone[5] = 23;
2081         cone[6] = 24;
2082         cone[7] = 17;
2083         PetscCall(DMPlexSetCone(dm, 11, cone));
2084         cone[0] = 20;
2085         cone[1] = 28;
2086         cone[2] = 26;
2087         cone[3] = 21;
2088         cone[4] = 18;
2089         cone[5] = 17;
2090         cone[6] = 24;
2091         cone[7] = 27;
2092         PetscCall(DMPlexSetCone(dm, 12, cone));
2093         cone[0] = 30;
2094         cone[1] = 28;
2095         cone[2] = 20;
2096         cone[3] = 19;
2097         cone[4] = 29;
2098         cone[5] = 15;
2099         cone[6] = 18;
2100         cone[7] = 27;
2101         PetscCall(DMPlexSetCone(dm, 13, cone));
2102         cone[0] = 30;
2103         cone[1] = 19;
2104         cone[2] = 22;
2105         cone[3] = 25;
2106         cone[4] = 29;
2107         cone[5] = 23;
2108         cone[6] = 16;
2109         cone[7] = 15;
2110         PetscCall(DMPlexSetCone(dm, 14, cone));
2111       } else {
2112         cone[0] = 5;
2113         cone[1] = 8;
2114         cone[2] = 7;
2115         cone[3] = 6;
2116         cone[4] = 9;
2117         cone[5] = 12;
2118         cone[6] = 11;
2119         cone[7] = 10;
2120         PetscCall(DMPlexSetCone(dm, 0, cone));
2121         cone[0] = 6;
2122         cone[1] = 7;
2123         cone[2] = 14;
2124         cone[3] = 13;
2125         cone[4] = 12;
2126         cone[5] = 15;
2127         cone[6] = 16;
2128         cone[7] = 11;
2129         PetscCall(DMPlexSetCone(dm, 1, cone));
2130         cone[0] = 8;
2131         cone[1] = 17;
2132         cone[2] = 14;
2133         cone[3] = 7;
2134         cone[4] = 10;
2135         cone[5] = 11;
2136         cone[6] = 16;
2137         cone[7] = 18;
2138         PetscCall(DMPlexSetCone(dm, 2, cone));
2139         cone[0] = 19;
2140         cone[1] = 17;
2141         cone[2] = 8;
2142         cone[3] = 5;
2143         cone[4] = 20;
2144         cone[5] = 9;
2145         cone[6] = 10;
2146         cone[7] = 18;
2147         PetscCall(DMPlexSetCone(dm, 3, cone));
2148         cone[0] = 19;
2149         cone[1] = 5;
2150         cone[2] = 6;
2151         cone[3] = 13;
2152         cone[4] = 20;
2153         cone[5] = 15;
2154         cone[6] = 12;
2155         cone[7] = 9;
2156         PetscCall(DMPlexSetCone(dm, 4, cone));
2157       }
2158     }
2159     PetscCall(DMPlexSymmetrize(dm));
2160     PetscCall(DMPlexStratify(dm));
2161   }
2162   /* Create cube geometry */
2163   {
2164     Vec             coordinates;
2165     PetscSection    coordSection;
2166     PetscScalar    *coords;
2167     PetscInt        coordSize, v;
2168     const PetscReal dis = 1.0 / PetscSqrtReal(2.0);
2169     const PetscReal ds2 = dis / 2.0;
2170 
2171     /* Build coordinates */
2172     PetscCall(DMGetCoordinateSection(dm, &coordSection));
2173     PetscCall(PetscSectionSetNumFields(coordSection, 1));
2174     PetscCall(PetscSectionSetFieldComponents(coordSection, 0, dim));
2175     PetscCall(PetscSectionSetChart(coordSection, numCells, numCells + numVertices));
2176     for (v = numCells; v < numCells + numVertices; ++v) {
2177       PetscCall(PetscSectionSetDof(coordSection, v, dim));
2178       PetscCall(PetscSectionSetFieldDof(coordSection, v, 0, dim));
2179     }
2180     PetscCall(PetscSectionSetUp(coordSection));
2181     PetscCall(PetscSectionGetStorageSize(coordSection, &coordSize));
2182     PetscCall(VecCreate(PETSC_COMM_SELF, &coordinates));
2183     PetscCall(PetscObjectSetName((PetscObject)coordinates, "coordinates"));
2184     PetscCall(VecSetSizes(coordinates, coordSize, PETSC_DETERMINE));
2185     PetscCall(VecSetBlockSize(coordinates, dim));
2186     PetscCall(VecSetType(coordinates, VECSTANDARD));
2187     PetscCall(VecGetArray(coordinates, &coords));
2188     if (rank == 0) {
2189       coords[0 * dim + 0]  = -ds2;
2190       coords[0 * dim + 1]  = -ds2;
2191       coords[0 * dim + 2]  = 0.0;
2192       coords[1 * dim + 0]  = ds2;
2193       coords[1 * dim + 1]  = -ds2;
2194       coords[1 * dim + 2]  = 0.0;
2195       coords[2 * dim + 0]  = ds2;
2196       coords[2 * dim + 1]  = ds2;
2197       coords[2 * dim + 2]  = 0.0;
2198       coords[3 * dim + 0]  = -ds2;
2199       coords[3 * dim + 1]  = ds2;
2200       coords[3 * dim + 2]  = 0.0;
2201       coords[4 * dim + 0]  = -ds2;
2202       coords[4 * dim + 1]  = -ds2;
2203       coords[4 * dim + 2]  = 1.0;
2204       coords[5 * dim + 0]  = -ds2;
2205       coords[5 * dim + 1]  = ds2;
2206       coords[5 * dim + 2]  = 1.0;
2207       coords[6 * dim + 0]  = ds2;
2208       coords[6 * dim + 1]  = ds2;
2209       coords[6 * dim + 2]  = 1.0;
2210       coords[7 * dim + 0]  = ds2;
2211       coords[7 * dim + 1]  = -ds2;
2212       coords[7 * dim + 2]  = 1.0;
2213       coords[8 * dim + 0]  = dis;
2214       coords[8 * dim + 1]  = -dis;
2215       coords[8 * dim + 2]  = 0.0;
2216       coords[9 * dim + 0]  = dis;
2217       coords[9 * dim + 1]  = dis;
2218       coords[9 * dim + 2]  = 0.0;
2219       coords[10 * dim + 0] = dis;
2220       coords[10 * dim + 1] = -dis;
2221       coords[10 * dim + 2] = 1.0;
2222       coords[11 * dim + 0] = dis;
2223       coords[11 * dim + 1] = dis;
2224       coords[11 * dim + 2] = 1.0;
2225       coords[12 * dim + 0] = -dis;
2226       coords[12 * dim + 1] = dis;
2227       coords[12 * dim + 2] = 0.0;
2228       coords[13 * dim + 0] = -dis;
2229       coords[13 * dim + 1] = dis;
2230       coords[13 * dim + 2] = 1.0;
2231       coords[14 * dim + 0] = -dis;
2232       coords[14 * dim + 1] = -dis;
2233       coords[14 * dim + 2] = 0.0;
2234       coords[15 * dim + 0] = -dis;
2235       coords[15 * dim + 1] = -dis;
2236       coords[15 * dim + 2] = 1.0;
2237       if (periodicZ == DM_BOUNDARY_PERIODIC) {
2238         /* 15 31 19 */ coords[16 * dim + 0] = -ds2;
2239         coords[16 * dim + 1]                = -ds2;
2240         coords[16 * dim + 2]                = 0.5;
2241         /* 16 32 22 */ coords[17 * dim + 0] = ds2;
2242         coords[17 * dim + 1]                = -ds2;
2243         coords[17 * dim + 2]                = 0.5;
2244         /* 17 33 21 */ coords[18 * dim + 0] = ds2;
2245         coords[18 * dim + 1]                = ds2;
2246         coords[18 * dim + 2]                = 0.5;
2247         /* 18 34 20 */ coords[19 * dim + 0] = -ds2;
2248         coords[19 * dim + 1]                = ds2;
2249         coords[19 * dim + 2]                = 0.5;
2250         /* 29 35 30 */ coords[20 * dim + 0] = -dis;
2251         coords[20 * dim + 1]                = -dis;
2252         coords[20 * dim + 2]                = 0.5;
2253         /* 23 36 25 */ coords[21 * dim + 0] = dis;
2254         coords[21 * dim + 1]                = -dis;
2255         coords[21 * dim + 2]                = 0.5;
2256         /* 24 37 26 */ coords[22 * dim + 0] = dis;
2257         coords[22 * dim + 1]                = dis;
2258         coords[22 * dim + 2]                = 0.5;
2259         /* 27 38 28 */ coords[23 * dim + 0] = -dis;
2260         coords[23 * dim + 1]                = dis;
2261         coords[23 * dim + 2]                = 0.5;
2262       }
2263     }
2264     PetscCall(VecRestoreArray(coordinates, &coords));
2265     PetscCall(DMSetCoordinatesLocal(dm, coordinates));
2266     PetscCall(VecDestroy(&coordinates));
2267   }
2268   /* Create periodicity */
2269   if (periodicZ == DM_BOUNDARY_PERIODIC || periodicZ == DM_BOUNDARY_TWIST) {
2270     PetscReal L[3]       = {-1., -1., 0.};
2271     PetscReal maxCell[3] = {-1., -1., 0.};
2272     PetscReal lower[3]   = {0.0, 0.0, 0.0};
2273     PetscReal upper[3]   = {1.0, 1.0, 1.5};
2274     PetscInt  numZCells  = 3;
2275 
2276     L[2]       = upper[2] - lower[2];
2277     maxCell[2] = 1.1 * (L[2] / numZCells);
2278     PetscCall(DMSetPeriodicity(dm, maxCell, lower, L));
2279   }
2280   {
2281     DM          cdm;
2282     PetscDS     cds;
2283     PetscScalar c[2] = {1.0, 1.0};
2284 
2285     PetscCall(DMPlexCreateCoordinateSpace(dm, 1, PETSC_TRUE, NULL));
2286     PetscCall(DMGetCoordinateDM(dm, &cdm));
2287     PetscCall(DMGetDS(cdm, &cds));
2288     PetscCall(PetscDSSetConstants(cds, 2, c));
2289   }
2290   PetscCall(PetscLogEventEnd(DMPLEX_Generate, dm, 0, 0, 0));
2291 
2292   /* Wait for coordinate creation before doing in-place modification */
2293   PetscCall(DMPlexInterpolateInPlace_Internal(dm));
2294 
2295   char        oldprefix[PETSC_MAX_PATH_LEN];
2296   const char *prefix;
2297 
2298   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
2299   PetscCall(PetscStrncpy(oldprefix, prefix, PETSC_MAX_PATH_LEN));
2300   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)dm, "petsc_cyl_ref_"));
2301   for (PetscInt r = 0; r < PetscMax(0, Nr); ++r) {
2302     DM rdm;
2303 
2304     PetscCall(DMRefine(dm, PetscObjectComm((PetscObject)dm), &rdm));
2305     PetscCall(DMPlexReplace_Internal(dm, &rdm));
2306   }
2307   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)dm, oldprefix));
2308   PetscCall(DMPlexRemapGeometry(dm, 0.0, snapToCylinder));
2309 
2310   DMLabel         bdlabel, edgelabel;
2311   IS              faceIS;
2312   const PetscInt *faces;
2313   PetscInt        Nf;
2314 
2315   PetscCall(DMCreateLabel(dm, "marker"));
2316   PetscCall(DMGetLabel(dm, "marker", &bdlabel));
2317   PetscCall(DMCreateLabel(dm, "generatrix"));
2318   PetscCall(DMGetLabel(dm, "generatrix", &edgelabel));
2319   PetscCall(DMPlexMarkBoundaryFaces(dm, PETSC_DETERMINE, bdlabel));
2320   // Remove faces on top and bottom
2321   PetscCall(DMLabelGetStratumIS(bdlabel, 1, &faceIS));
2322   if (faceIS) {
2323     PetscCall(ISGetLocalSize(faceIS, &Nf));
2324     PetscCall(ISGetIndices(faceIS, &faces));
2325     for (PetscInt f = 0; f < Nf; ++f) {
2326       PetscReal vol, normal[3];
2327 
2328       PetscCall(DMPlexComputeCellGeometryFVM(dm, faces[f], &vol, NULL, normal));
2329       if (PetscAbsReal(normal[2]) < PETSC_SMALL) PetscCall(DMLabelSetValue(edgelabel, faces[f], 1));
2330     }
2331     PetscCall(ISRestoreIndices(faceIS, &faces));
2332     PetscCall(ISDestroy(&faceIS));
2333   }
2334   PetscCall(DMPlexLabelComplete(dm, bdlabel));
2335   PetscCall(DMPlexLabelComplete(dm, edgelabel));
2336   PetscFunctionReturn(PETSC_SUCCESS);
2337 }
2338 
2339 /*@
2340   DMPlexCreateHexCylinderMesh - Creates a mesh on the tensor product of the unit interval with the circle (cylinder) using hexahedra.
2341 
2342   Collective
2343 
2344   Input Parameters:
2345 + comm      - The communicator for the `DM` object
2346 . periodicZ - The boundary type for the Z direction
2347 - Nr        - The number of refinements to carry out
2348 
2349   Output Parameter:
2350 . dm - The `DM` object
2351 
2352   Level: beginner
2353 
2354   Note:
2355   Here is the output numbering looking from the bottom of the cylinder\:
2356 .vb
2357        17-----14
2358         |     |
2359         |  2  |
2360         |     |
2361  17-----8-----7-----14
2362   |     |     |     |
2363   |  3  |  0  |  1  |
2364   |     |     |     |
2365  19-----5-----6-----13
2366         |     |
2367         |  4  |
2368         |     |
2369        19-----13
2370 
2371  and up through the top
2372 
2373        18-----16
2374         |     |
2375         |  2  |
2376         |     |
2377  18----10----11-----16
2378   |     |     |     |
2379   |  3  |  0  |  1  |
2380   |     |     |     |
2381  20-----9----12-----15
2382         |     |
2383         |  4  |
2384         |     |
2385        20-----15
2386 .ve
2387 
2388 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateBoxMesh()`, `DMSetType()`, `DMCreate()`
2389 @*/
2390 PetscErrorCode DMPlexCreateHexCylinderMesh(MPI_Comm comm, DMBoundaryType periodicZ, PetscInt Nr, DM *dm)
2391 {
2392   PetscFunctionBegin;
2393   PetscAssertPointer(dm, 4);
2394   PetscCall(DMCreate(comm, dm));
2395   PetscCall(DMSetType(*dm, DMPLEX));
2396   PetscCall(DMPlexCreateHexCylinderMesh_Internal(*dm, periodicZ, Nr));
2397   PetscFunctionReturn(PETSC_SUCCESS);
2398 }
2399 
2400 static PetscErrorCode DMPlexCreateWedgeCylinderMesh_Internal(DM dm, PetscInt n, PetscBool interpolate)
2401 {
2402   const PetscInt dim = 3;
2403   PetscInt       numCells, numVertices, v;
2404   PetscMPIInt    rank;
2405 
2406   PetscFunctionBegin;
2407   PetscCheck(n >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of wedges %" PetscInt_FMT " cannot be negative", n);
2408   PetscCall(PetscLogEventBegin(DMPLEX_Generate, dm, 0, 0, 0));
2409   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
2410   PetscCall(DMSetDimension(dm, dim));
2411   /* Must create the celltype label here so that we do not automatically try to compute the types */
2412   PetscCall(DMCreateLabel(dm, "celltype"));
2413   /* Create topology */
2414   {
2415     PetscInt cone[6], c;
2416 
2417     numCells    = rank == 0 ? n : 0;
2418     numVertices = rank == 0 ? 2 * (n + 1) : 0;
2419     PetscCall(DMPlexSetChart(dm, 0, numCells + numVertices));
2420     for (c = 0; c < numCells; c++) PetscCall(DMPlexSetConeSize(dm, c, 6));
2421     PetscCall(DMSetUp(dm));
2422     for (c = 0; c < numCells; c++) {
2423       cone[0] = c + n * 1;
2424       cone[1] = (c + 1) % n + n * 1;
2425       cone[2] = 0 + 3 * n;
2426       cone[3] = c + n * 2;
2427       cone[4] = (c + 1) % n + n * 2;
2428       cone[5] = 1 + 3 * n;
2429       PetscCall(DMPlexSetCone(dm, c, cone));
2430       PetscCall(DMPlexSetCellType(dm, c, DM_POLYTOPE_TRI_PRISM_TENSOR));
2431     }
2432     PetscCall(DMPlexSymmetrize(dm));
2433     PetscCall(DMPlexStratify(dm));
2434   }
2435   for (v = numCells; v < numCells + numVertices; ++v) PetscCall(DMPlexSetCellType(dm, v, DM_POLYTOPE_POINT));
2436   /* Create cylinder geometry */
2437   {
2438     Vec          coordinates;
2439     PetscSection coordSection;
2440     PetscScalar *coords;
2441     PetscInt     coordSize, c;
2442 
2443     /* Build coordinates */
2444     PetscCall(DMGetCoordinateSection(dm, &coordSection));
2445     PetscCall(PetscSectionSetNumFields(coordSection, 1));
2446     PetscCall(PetscSectionSetFieldComponents(coordSection, 0, dim));
2447     PetscCall(PetscSectionSetChart(coordSection, numCells, numCells + numVertices));
2448     for (v = numCells; v < numCells + numVertices; ++v) {
2449       PetscCall(PetscSectionSetDof(coordSection, v, dim));
2450       PetscCall(PetscSectionSetFieldDof(coordSection, v, 0, dim));
2451     }
2452     PetscCall(PetscSectionSetUp(coordSection));
2453     PetscCall(PetscSectionGetStorageSize(coordSection, &coordSize));
2454     PetscCall(VecCreate(PETSC_COMM_SELF, &coordinates));
2455     PetscCall(PetscObjectSetName((PetscObject)coordinates, "coordinates"));
2456     PetscCall(VecSetSizes(coordinates, coordSize, PETSC_DETERMINE));
2457     PetscCall(VecSetBlockSize(coordinates, dim));
2458     PetscCall(VecSetType(coordinates, VECSTANDARD));
2459     PetscCall(VecGetArray(coordinates, &coords));
2460     for (c = 0; c < numCells; c++) {
2461       coords[(c + 0 * n) * dim + 0] = PetscCosReal(2.0 * c * PETSC_PI / n);
2462       coords[(c + 0 * n) * dim + 1] = PetscSinReal(2.0 * c * PETSC_PI / n);
2463       coords[(c + 0 * n) * dim + 2] = 1.0;
2464       coords[(c + 1 * n) * dim + 0] = PetscCosReal(2.0 * c * PETSC_PI / n);
2465       coords[(c + 1 * n) * dim + 1] = PetscSinReal(2.0 * c * PETSC_PI / n);
2466       coords[(c + 1 * n) * dim + 2] = 0.0;
2467     }
2468     if (rank == 0) {
2469       coords[(2 * n + 0) * dim + 0] = 0.0;
2470       coords[(2 * n + 0) * dim + 1] = 0.0;
2471       coords[(2 * n + 0) * dim + 2] = 1.0;
2472       coords[(2 * n + 1) * dim + 0] = 0.0;
2473       coords[(2 * n + 1) * dim + 1] = 0.0;
2474       coords[(2 * n + 1) * dim + 2] = 0.0;
2475     }
2476     PetscCall(VecRestoreArray(coordinates, &coords));
2477     PetscCall(DMSetCoordinatesLocal(dm, coordinates));
2478     PetscCall(VecDestroy(&coordinates));
2479   }
2480   PetscCall(PetscLogEventEnd(DMPLEX_Generate, dm, 0, 0, 0));
2481   /* Interpolate */
2482   if (interpolate) PetscCall(DMPlexInterpolateInPlace_Internal(dm));
2483   PetscFunctionReturn(PETSC_SUCCESS);
2484 }
2485 
2486 /*@
2487   DMPlexCreateWedgeCylinderMesh - Creates a mesh on the tensor product of the unit interval with the circle (cylinder) using wedges.
2488 
2489   Collective
2490 
2491   Input Parameters:
2492 + comm        - The communicator for the `DM` object
2493 . n           - The number of wedges around the origin
2494 - interpolate - Create edges and faces
2495 
2496   Output Parameter:
2497 . dm - The `DM` object
2498 
2499   Level: beginner
2500 
2501 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateHexCylinderMesh()`, `DMPlexCreateBoxMesh()`, `DMSetType()`, `DMCreate()`
2502 @*/
2503 PetscErrorCode DMPlexCreateWedgeCylinderMesh(MPI_Comm comm, PetscInt n, PetscBool interpolate, DM *dm)
2504 {
2505   PetscFunctionBegin;
2506   PetscAssertPointer(dm, 4);
2507   PetscCall(DMCreate(comm, dm));
2508   PetscCall(DMSetType(*dm, DMPLEX));
2509   PetscCall(DMPlexCreateWedgeCylinderMesh_Internal(*dm, n, interpolate));
2510   PetscFunctionReturn(PETSC_SUCCESS);
2511 }
2512 
2513 static inline PetscReal DiffNormReal(PetscInt dim, const PetscReal x[], const PetscReal y[])
2514 {
2515   PetscReal prod = 0.0;
2516   PetscInt  i;
2517   for (i = 0; i < dim; ++i) prod += PetscSqr(x[i] - y[i]);
2518   return PetscSqrtReal(prod);
2519 }
2520 
2521 static inline PetscReal DotReal(PetscInt dim, const PetscReal x[], const PetscReal y[])
2522 {
2523   PetscReal prod = 0.0;
2524   PetscInt  i;
2525   for (i = 0; i < dim; ++i) prod += x[i] * y[i];
2526   return prod;
2527 }
2528 
2529 /* The first constant is the sphere radius */
2530 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[])
2531 {
2532   PetscReal r     = PetscRealPart(constants[0]);
2533   PetscReal norm2 = 0.0, fac;
2534   PetscInt  n     = uOff[1] - uOff[0], d;
2535 
2536   for (d = 0; d < n; ++d) norm2 += PetscSqr(PetscRealPart(u[d]));
2537   fac = r / PetscSqrtReal(norm2);
2538   for (d = 0; d < n; ++d) f0[d] = u[d] * fac;
2539 }
2540 
2541 static PetscErrorCode DMPlexCreateSphereMesh_Internal(DM dm, PetscInt dim, PetscBool simplex, PetscReal R)
2542 {
2543   const PetscInt embedDim = dim + 1;
2544   PetscSection   coordSection;
2545   Vec            coordinates;
2546   PetscScalar   *coords;
2547   PetscReal     *coordsIn;
2548   PetscInt       numCells, numEdges, numVerts = 0, firstVertex = 0, v, firstEdge, coordSize, d, e;
2549   PetscMPIInt    rank;
2550 
2551   PetscFunctionBegin;
2552   PetscValidLogicalCollectiveBool(dm, simplex, 3);
2553   PetscCall(PetscLogEventBegin(DMPLEX_Generate, dm, 0, 0, 0));
2554   PetscCall(DMSetDimension(dm, dim));
2555   PetscCall(DMSetCoordinateDim(dm, dim + 1));
2556   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
2557   switch (dim) {
2558   case 1:
2559     numCells = 16;
2560     numVerts = numCells;
2561 
2562     // Build Topology
2563     PetscCall(DMPlexSetChart(dm, 0, numCells + numVerts));
2564     for (PetscInt c = 0; c < numCells; c++) PetscCall(DMPlexSetConeSize(dm, c, embedDim));
2565     PetscCall(DMSetUp(dm));
2566     for (PetscInt c = 0; c < numCells; ++c) {
2567       PetscInt cone[2];
2568 
2569       cone[0] = c + numCells;
2570       cone[1] = (c + 1) % numVerts + numCells;
2571       PetscCall(DMPlexSetCone(dm, c, cone));
2572     }
2573     PetscCall(DMPlexSymmetrize(dm));
2574     PetscCall(DMPlexStratify(dm));
2575     PetscCall(PetscMalloc1(numVerts * embedDim, &coordsIn));
2576     for (PetscInt v = 0; v < numVerts; ++v) {
2577       const PetscReal rad = 2. * PETSC_PI * v / numVerts;
2578 
2579       coordsIn[v * embedDim + 0] = PetscCosReal(rad);
2580       coordsIn[v * embedDim + 1] = PetscSinReal(rad);
2581     }
2582     break;
2583   case 2:
2584     if (simplex) {
2585       const PetscReal radius    = PetscSqrtReal(1 + PETSC_PHI * PETSC_PHI) / (1.0 + PETSC_PHI);
2586       const PetscReal edgeLen   = 2.0 / (1.0 + PETSC_PHI) * (R / radius);
2587       const PetscInt  degree    = 5;
2588       PetscReal       vertex[3] = {0.0, 1.0 / (1.0 + PETSC_PHI), PETSC_PHI / (1.0 + PETSC_PHI)};
2589       PetscInt        s[3]      = {1, 1, 1};
2590       PetscInt        cone[3];
2591       PetscInt       *graph;
2592 
2593       vertex[0] *= R / radius;
2594       vertex[1] *= R / radius;
2595       vertex[2] *= R / radius;
2596       numCells    = rank == 0 ? 20 : 0;
2597       numVerts    = rank == 0 ? 12 : 0;
2598       firstVertex = numCells;
2599       /* Use icosahedron, which for a R-sphere has coordinates which are all cyclic permutations of
2600 
2601            (0, \pm 1/\phi+1, \pm \phi/\phi+1)
2602 
2603          where \phi^2 - \phi - 1 = 0, meaning \phi is the golden ratio \frac{1 + \sqrt{5}}{2}. The edge
2604          length is then given by 2/(1+\phi) = 2 * 0.38197 = 0.76393.
2605       */
2606       /* Construct vertices */
2607       PetscCall(PetscCalloc1(numVerts * embedDim, &coordsIn));
2608       if (rank == 0) {
2609         for (PetscInt p = 0, i = 0; p < embedDim; ++p) {
2610           for (s[1] = -1; s[1] < 2; s[1] += 2) {
2611             for (s[2] = -1; s[2] < 2; s[2] += 2) {
2612               for (d = 0; d < embedDim; ++d) coordsIn[i * embedDim + d] = s[(d + p) % embedDim] * vertex[(d + p) % embedDim];
2613               ++i;
2614             }
2615           }
2616         }
2617       }
2618       /* Construct graph */
2619       PetscCall(PetscCalloc1(numVerts * numVerts, &graph));
2620       for (PetscInt i = 0; i < numVerts; ++i) {
2621         PetscInt k = 0;
2622         for (PetscInt j = 0; j < numVerts; ++j) {
2623           if (PetscAbsReal(DiffNormReal(embedDim, &coordsIn[i * embedDim], &coordsIn[j * embedDim]) - edgeLen) < PETSC_SMALL) {
2624             graph[i * numVerts + j] = 1;
2625             ++k;
2626           }
2627         }
2628         PetscCheck(k == degree, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid icosahedron, vertex %" PetscInt_FMT " degree %" PetscInt_FMT " != %" PetscInt_FMT, i, k, degree);
2629       }
2630       /* Build Topology */
2631       PetscCall(DMPlexSetChart(dm, 0, numCells + numVerts));
2632       for (PetscInt c = 0; c < numCells; c++) PetscCall(DMPlexSetConeSize(dm, c, embedDim));
2633       PetscCall(DMSetUp(dm)); /* Allocate space for cones */
2634       /* Cells */
2635       for (PetscInt i = 0, c = 0; i < numVerts; ++i) {
2636         for (PetscInt j = 0; j < i; ++j) {
2637           for (PetscInt k = 0; k < j; ++k) {
2638             if (graph[i * numVerts + j] && graph[j * numVerts + k] && graph[k * numVerts + i]) {
2639               cone[0] = firstVertex + i;
2640               cone[1] = firstVertex + j;
2641               cone[2] = firstVertex + k;
2642               /* Check orientation */
2643               {
2644                 const PetscInt epsilon[3][3][3] = {
2645                   {{0, 0, 0},  {0, 0, 1},  {0, -1, 0}},
2646                   {{0, 0, -1}, {0, 0, 0},  {1, 0, 0} },
2647                   {{0, 1, 0},  {-1, 0, 0}, {0, 0, 0} }
2648                 };
2649                 PetscReal normal[3];
2650                 PetscInt  e, f;
2651 
2652                 for (d = 0; d < embedDim; ++d) {
2653                   normal[d] = 0.0;
2654                   for (e = 0; e < embedDim; ++e) {
2655                     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]);
2656                   }
2657                 }
2658                 if (DotReal(embedDim, normal, &coordsIn[i * embedDim]) < 0) {
2659                   PetscInt tmp = cone[1];
2660                   cone[1]      = cone[2];
2661                   cone[2]      = tmp;
2662                 }
2663               }
2664               PetscCall(DMPlexSetCone(dm, c++, cone));
2665             }
2666           }
2667         }
2668       }
2669       PetscCall(DMPlexSymmetrize(dm));
2670       PetscCall(DMPlexStratify(dm));
2671       PetscCall(PetscFree(graph));
2672     } else {
2673       /*
2674         12-21--13
2675          |     |
2676         25  4  24
2677          |     |
2678   12-25--9-16--8-24--13
2679    |     |     |     |
2680   23  5 17  0 15  3  22
2681    |     |     |     |
2682   10-20--6-14--7-19--11
2683          |     |
2684         20  1  19
2685          |     |
2686         10-18--11
2687          |     |
2688         23  2  22
2689          |     |
2690         12-21--13
2691        */
2692       PetscInt cone[4], ornt[4];
2693 
2694       numCells    = rank == 0 ? 6 : 0;
2695       numEdges    = rank == 0 ? 12 : 0;
2696       numVerts    = rank == 0 ? 8 : 0;
2697       firstVertex = numCells;
2698       firstEdge   = numCells + numVerts;
2699       /* Build Topology */
2700       PetscCall(DMPlexSetChart(dm, 0, numCells + numEdges + numVerts));
2701       for (PetscInt c = 0; c < numCells; c++) PetscCall(DMPlexSetConeSize(dm, c, 4));
2702       for (e = firstEdge; e < firstEdge + numEdges; ++e) PetscCall(DMPlexSetConeSize(dm, e, 2));
2703       PetscCall(DMSetUp(dm)); /* Allocate space for cones */
2704       if (rank == 0) {
2705         /* Cell 0 */
2706         cone[0] = 14;
2707         cone[1] = 15;
2708         cone[2] = 16;
2709         cone[3] = 17;
2710         PetscCall(DMPlexSetCone(dm, 0, cone));
2711         ornt[0] = 0;
2712         ornt[1] = 0;
2713         ornt[2] = 0;
2714         ornt[3] = 0;
2715         PetscCall(DMPlexSetConeOrientation(dm, 0, ornt));
2716         /* Cell 1 */
2717         cone[0] = 18;
2718         cone[1] = 19;
2719         cone[2] = 14;
2720         cone[3] = 20;
2721         PetscCall(DMPlexSetCone(dm, 1, cone));
2722         ornt[0] = 0;
2723         ornt[1] = 0;
2724         ornt[2] = -1;
2725         ornt[3] = 0;
2726         PetscCall(DMPlexSetConeOrientation(dm, 1, ornt));
2727         /* Cell 2 */
2728         cone[0] = 21;
2729         cone[1] = 22;
2730         cone[2] = 18;
2731         cone[3] = 23;
2732         PetscCall(DMPlexSetCone(dm, 2, cone));
2733         ornt[0] = 0;
2734         ornt[1] = 0;
2735         ornt[2] = -1;
2736         ornt[3] = 0;
2737         PetscCall(DMPlexSetConeOrientation(dm, 2, ornt));
2738         /* Cell 3 */
2739         cone[0] = 19;
2740         cone[1] = 22;
2741         cone[2] = 24;
2742         cone[3] = 15;
2743         PetscCall(DMPlexSetCone(dm, 3, cone));
2744         ornt[0] = -1;
2745         ornt[1] = -1;
2746         ornt[2] = 0;
2747         ornt[3] = -1;
2748         PetscCall(DMPlexSetConeOrientation(dm, 3, ornt));
2749         /* Cell 4 */
2750         cone[0] = 16;
2751         cone[1] = 24;
2752         cone[2] = 21;
2753         cone[3] = 25;
2754         PetscCall(DMPlexSetCone(dm, 4, cone));
2755         ornt[0] = -1;
2756         ornt[1] = -1;
2757         ornt[2] = -1;
2758         ornt[3] = 0;
2759         PetscCall(DMPlexSetConeOrientation(dm, 4, ornt));
2760         /* Cell 5 */
2761         cone[0] = 20;
2762         cone[1] = 17;
2763         cone[2] = 25;
2764         cone[3] = 23;
2765         PetscCall(DMPlexSetCone(dm, 5, cone));
2766         ornt[0] = -1;
2767         ornt[1] = -1;
2768         ornt[2] = -1;
2769         ornt[3] = -1;
2770         PetscCall(DMPlexSetConeOrientation(dm, 5, ornt));
2771         /* Edges */
2772         cone[0] = 6;
2773         cone[1] = 7;
2774         PetscCall(DMPlexSetCone(dm, 14, cone));
2775         cone[0] = 7;
2776         cone[1] = 8;
2777         PetscCall(DMPlexSetCone(dm, 15, cone));
2778         cone[0] = 8;
2779         cone[1] = 9;
2780         PetscCall(DMPlexSetCone(dm, 16, cone));
2781         cone[0] = 9;
2782         cone[1] = 6;
2783         PetscCall(DMPlexSetCone(dm, 17, cone));
2784         cone[0] = 10;
2785         cone[1] = 11;
2786         PetscCall(DMPlexSetCone(dm, 18, cone));
2787         cone[0] = 11;
2788         cone[1] = 7;
2789         PetscCall(DMPlexSetCone(dm, 19, cone));
2790         cone[0] = 6;
2791         cone[1] = 10;
2792         PetscCall(DMPlexSetCone(dm, 20, cone));
2793         cone[0] = 12;
2794         cone[1] = 13;
2795         PetscCall(DMPlexSetCone(dm, 21, cone));
2796         cone[0] = 13;
2797         cone[1] = 11;
2798         PetscCall(DMPlexSetCone(dm, 22, cone));
2799         cone[0] = 10;
2800         cone[1] = 12;
2801         PetscCall(DMPlexSetCone(dm, 23, cone));
2802         cone[0] = 13;
2803         cone[1] = 8;
2804         PetscCall(DMPlexSetCone(dm, 24, cone));
2805         cone[0] = 12;
2806         cone[1] = 9;
2807         PetscCall(DMPlexSetCone(dm, 25, cone));
2808       }
2809       PetscCall(DMPlexSymmetrize(dm));
2810       PetscCall(DMPlexStratify(dm));
2811       /* Build coordinates */
2812       PetscCall(PetscCalloc1(numVerts * embedDim, &coordsIn));
2813       if (rank == 0) {
2814         coordsIn[0 * embedDim + 0] = -R;
2815         coordsIn[0 * embedDim + 1] = R;
2816         coordsIn[0 * embedDim + 2] = -R;
2817         coordsIn[1 * embedDim + 0] = R;
2818         coordsIn[1 * embedDim + 1] = R;
2819         coordsIn[1 * embedDim + 2] = -R;
2820         coordsIn[2 * embedDim + 0] = R;
2821         coordsIn[2 * embedDim + 1] = -R;
2822         coordsIn[2 * embedDim + 2] = -R;
2823         coordsIn[3 * embedDim + 0] = -R;
2824         coordsIn[3 * embedDim + 1] = -R;
2825         coordsIn[3 * embedDim + 2] = -R;
2826         coordsIn[4 * embedDim + 0] = -R;
2827         coordsIn[4 * embedDim + 1] = R;
2828         coordsIn[4 * embedDim + 2] = R;
2829         coordsIn[5 * embedDim + 0] = R;
2830         coordsIn[5 * embedDim + 1] = R;
2831         coordsIn[5 * embedDim + 2] = R;
2832         coordsIn[6 * embedDim + 0] = -R;
2833         coordsIn[6 * embedDim + 1] = -R;
2834         coordsIn[6 * embedDim + 2] = R;
2835         coordsIn[7 * embedDim + 0] = R;
2836         coordsIn[7 * embedDim + 1] = -R;
2837         coordsIn[7 * embedDim + 2] = R;
2838       }
2839     }
2840     break;
2841   case 3:
2842     if (simplex) {
2843       const PetscReal edgeLen         = 1.0 / PETSC_PHI;
2844       PetscReal       vertexA[4]      = {0.5, 0.5, 0.5, 0.5};
2845       PetscReal       vertexB[4]      = {1.0, 0.0, 0.0, 0.0};
2846       PetscReal       vertexC[4]      = {0.5, 0.5 * PETSC_PHI, 0.5 / PETSC_PHI, 0.0};
2847       const PetscInt  degree          = 12;
2848       PetscInt        s[4]            = {1, 1, 1};
2849       PetscInt        evenPerm[12][4] = {
2850         {0, 1, 2, 3},
2851         {0, 2, 3, 1},
2852         {0, 3, 1, 2},
2853         {1, 0, 3, 2},
2854         {1, 2, 0, 3},
2855         {1, 3, 2, 0},
2856         {2, 0, 1, 3},
2857         {2, 1, 3, 0},
2858         {2, 3, 0, 1},
2859         {3, 0, 2, 1},
2860         {3, 1, 0, 2},
2861         {3, 2, 1, 0}
2862       };
2863       PetscInt  cone[4];
2864       PetscInt *graph, p, i, j, k, l;
2865 
2866       vertexA[0] *= R;
2867       vertexA[1] *= R;
2868       vertexA[2] *= R;
2869       vertexA[3] *= R;
2870       vertexB[0] *= R;
2871       vertexB[1] *= R;
2872       vertexB[2] *= R;
2873       vertexB[3] *= R;
2874       vertexC[0] *= R;
2875       vertexC[1] *= R;
2876       vertexC[2] *= R;
2877       vertexC[3] *= R;
2878       numCells    = rank == 0 ? 600 : 0;
2879       numVerts    = rank == 0 ? 120 : 0;
2880       firstVertex = numCells;
2881       /* Use the 600-cell, which for a unit sphere has coordinates which are
2882 
2883            1/2 (\pm 1, \pm 1,    \pm 1, \pm 1)                          16
2884                (\pm 1,    0,       0,      0)  all cyclic permutations   8
2885            1/2 (\pm 1, \pm phi, \pm 1/phi, 0)  all even permutations    96
2886 
2887          where \phi^2 - \phi - 1 = 0, meaning \phi is the golden ratio \frac{1 + \sqrt{5}}{2}. The edge
2888          length is then given by 1/\phi = 0.61803.
2889 
2890          http://buzzard.pugetsound.edu/sage-practice/ch03s03.html
2891          http://mathworld.wolfram.com/600-Cell.html
2892       */
2893       /* Construct vertices */
2894       PetscCall(PetscCalloc1(numVerts * embedDim, &coordsIn));
2895       i = 0;
2896       if (rank == 0) {
2897         for (s[0] = -1; s[0] < 2; s[0] += 2) {
2898           for (s[1] = -1; s[1] < 2; s[1] += 2) {
2899             for (s[2] = -1; s[2] < 2; s[2] += 2) {
2900               for (s[3] = -1; s[3] < 2; s[3] += 2) {
2901                 for (d = 0; d < embedDim; ++d) coordsIn[i * embedDim + d] = s[d] * vertexA[d];
2902                 ++i;
2903               }
2904             }
2905           }
2906         }
2907         for (p = 0; p < embedDim; ++p) {
2908           s[1] = s[2] = s[3] = 1;
2909           for (s[0] = -1; s[0] < 2; s[0] += 2) {
2910             for (d = 0; d < embedDim; ++d) coordsIn[i * embedDim + d] = s[(d + p) % embedDim] * vertexB[(d + p) % embedDim];
2911             ++i;
2912           }
2913         }
2914         for (p = 0; p < 12; ++p) {
2915           s[3] = 1;
2916           for (s[0] = -1; s[0] < 2; s[0] += 2) {
2917             for (s[1] = -1; s[1] < 2; s[1] += 2) {
2918               for (s[2] = -1; s[2] < 2; s[2] += 2) {
2919                 for (d = 0; d < embedDim; ++d) coordsIn[i * embedDim + d] = s[evenPerm[p][d]] * vertexC[evenPerm[p][d]];
2920                 ++i;
2921               }
2922             }
2923           }
2924         }
2925       }
2926       PetscCheck(i == numVerts, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid 600-cell, vertices %" PetscInt_FMT " != %" PetscInt_FMT, i, numVerts);
2927       /* Construct graph */
2928       PetscCall(PetscCalloc1(numVerts * numVerts, &graph));
2929       for (i = 0; i < numVerts; ++i) {
2930         for (j = 0, k = 0; j < numVerts; ++j) {
2931           if (PetscAbsReal(DiffNormReal(embedDim, &coordsIn[i * embedDim], &coordsIn[j * embedDim]) - edgeLen) < PETSC_SMALL) {
2932             graph[i * numVerts + j] = 1;
2933             ++k;
2934           }
2935         }
2936         PetscCheck(k == degree, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid 600-cell, vertex %" PetscInt_FMT " degree %" PetscInt_FMT " != %" PetscInt_FMT, i, k, degree);
2937       }
2938       /* Build Topology */
2939       PetscCall(DMPlexSetChart(dm, 0, numCells + numVerts));
2940       for (PetscInt c = 0; c < numCells; c++) PetscCall(DMPlexSetConeSize(dm, c, embedDim));
2941       PetscCall(DMSetUp(dm)); /* Allocate space for cones */
2942       /* Cells */
2943       if (rank == 0) {
2944         for (PetscInt i = 0, c = 0; i < numVerts; ++i) {
2945           for (j = 0; j < i; ++j) {
2946             for (k = 0; k < j; ++k) {
2947               for (l = 0; l < k; ++l) {
2948                 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]) {
2949                   cone[0] = firstVertex + i;
2950                   cone[1] = firstVertex + j;
2951                   cone[2] = firstVertex + k;
2952                   cone[3] = firstVertex + l;
2953                   /* Check orientation: https://ef.gy/linear-algebra:normal-vectors-in-higher-dimensional-spaces */
2954                   {
2955                     const PetscInt epsilon[4][4][4][4] = {
2956                       {{{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}}},
2957 
2958                       {{{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}}},
2959 
2960                       {{{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}}},
2961 
2962                       {{{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}} }
2963                     };
2964                     PetscReal normal[4];
2965                     PetscInt  e, f, g;
2966 
2967                     for (d = 0; d < embedDim; ++d) {
2968                       normal[d] = 0.0;
2969                       for (e = 0; e < embedDim; ++e) {
2970                         for (f = 0; f < embedDim; ++f) {
2971                           for (g = 0; g < embedDim; ++g) {
2972                             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]);
2973                           }
2974                         }
2975                       }
2976                     }
2977                     if (DotReal(embedDim, normal, &coordsIn[i * embedDim]) < 0) {
2978                       PetscInt tmp = cone[1];
2979                       cone[1]      = cone[2];
2980                       cone[2]      = tmp;
2981                     }
2982                   }
2983                   PetscCall(DMPlexSetCone(dm, c++, cone));
2984                 }
2985               }
2986             }
2987           }
2988         }
2989       }
2990       PetscCall(DMPlexSymmetrize(dm));
2991       PetscCall(DMPlexStratify(dm));
2992       PetscCall(PetscFree(graph));
2993     }
2994     break;
2995   default:
2996     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Unsupported dimension for sphere: %" PetscInt_FMT, dim);
2997   }
2998   /* Create coordinates */
2999   PetscCall(DMGetCoordinateSection(dm, &coordSection));
3000   PetscCall(PetscSectionSetNumFields(coordSection, 1));
3001   PetscCall(PetscSectionSetFieldComponents(coordSection, 0, embedDim));
3002   PetscCall(PetscSectionSetChart(coordSection, firstVertex, firstVertex + numVerts));
3003   for (v = firstVertex; v < firstVertex + numVerts; ++v) {
3004     PetscCall(PetscSectionSetDof(coordSection, v, embedDim));
3005     PetscCall(PetscSectionSetFieldDof(coordSection, v, 0, embedDim));
3006   }
3007   PetscCall(PetscSectionSetUp(coordSection));
3008   PetscCall(PetscSectionGetStorageSize(coordSection, &coordSize));
3009   PetscCall(VecCreate(PETSC_COMM_SELF, &coordinates));
3010   PetscCall(VecSetBlockSize(coordinates, embedDim));
3011   PetscCall(PetscObjectSetName((PetscObject)coordinates, "coordinates"));
3012   PetscCall(VecSetSizes(coordinates, coordSize, PETSC_DETERMINE));
3013   PetscCall(VecSetType(coordinates, VECSTANDARD));
3014   PetscCall(VecGetArray(coordinates, &coords));
3015   for (v = 0; v < numVerts; ++v)
3016     for (d = 0; d < embedDim; ++d) coords[v * embedDim + d] = coordsIn[v * embedDim + d];
3017   PetscCall(VecRestoreArray(coordinates, &coords));
3018   PetscCall(DMSetCoordinatesLocal(dm, coordinates));
3019   PetscCall(VecDestroy(&coordinates));
3020   PetscCall(PetscFree(coordsIn));
3021   {
3022     DM          cdm;
3023     PetscDS     cds;
3024     PetscScalar c = R;
3025 
3026     PetscCall(DMPlexCreateCoordinateSpace(dm, 1, PETSC_TRUE, snapToSphere));
3027     PetscCall(DMGetCoordinateDM(dm, &cdm));
3028     PetscCall(DMGetDS(cdm, &cds));
3029     PetscCall(PetscDSSetConstants(cds, 1, &c));
3030   }
3031   PetscCall(PetscLogEventEnd(DMPLEX_Generate, dm, 0, 0, 0));
3032   /* Wait for coordinate creation before doing in-place modification */
3033   if (simplex) PetscCall(DMPlexInterpolateInPlace_Internal(dm));
3034   PetscFunctionReturn(PETSC_SUCCESS);
3035 }
3036 
3037 typedef void (*TPSEvaluateFunc)(const PetscReal[], PetscReal *, PetscReal[], PetscReal (*)[3]);
3038 
3039 /*
3040  The Schwarz P implicit surface is
3041 
3042      f(x) = cos(x0) + cos(x1) + cos(x2) = 0
3043 */
3044 static void TPSEvaluate_SchwarzP(const PetscReal y[3], PetscReal *f, PetscReal grad[], PetscReal (*hess)[3])
3045 {
3046   PetscReal c[3] = {PetscCosReal(y[0] * PETSC_PI), PetscCosReal(y[1] * PETSC_PI), PetscCosReal(y[2] * PETSC_PI)};
3047   PetscReal g[3] = {-PetscSinReal(y[0] * PETSC_PI), -PetscSinReal(y[1] * PETSC_PI), -PetscSinReal(y[2] * PETSC_PI)};
3048   f[0]           = c[0] + c[1] + c[2];
3049   for (PetscInt i = 0; i < 3; i++) {
3050     grad[i] = PETSC_PI * g[i];
3051     for (PetscInt j = 0; j < 3; j++) hess[i][j] = (i == j) ? -PetscSqr(PETSC_PI) * c[i] : 0.;
3052   }
3053 }
3054 
3055 // u[] is a tentative normal on input. Replace with the implicit function gradient in the same direction
3056 static PetscErrorCode TPSExtrudeNormalFunc_SchwarzP(PetscInt dim, PetscReal time, const PetscReal x[], PetscInt r, PetscScalar u[], void *ctx)
3057 {
3058   for (PetscInt i = 0; i < 3; i++) u[i] = -PETSC_PI * PetscSinReal(x[i] * PETSC_PI);
3059   return PETSC_SUCCESS;
3060 }
3061 
3062 /*
3063  The Gyroid implicit surface is
3064 
3065  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)
3066 
3067 */
3068 static void TPSEvaluate_Gyroid(const PetscReal y[3], PetscReal *f, PetscReal grad[], PetscReal (*hess)[3])
3069 {
3070   PetscReal s[3] = {PetscSinReal(PETSC_PI * y[0]), PetscSinReal(PETSC_PI * (y[1] + .5)), PetscSinReal(PETSC_PI * (y[2] + .25))};
3071   PetscReal c[3] = {PetscCosReal(PETSC_PI * y[0]), PetscCosReal(PETSC_PI * (y[1] + .5)), PetscCosReal(PETSC_PI * (y[2] + .25))};
3072   f[0]           = s[0] * c[1] + s[1] * c[2] + s[2] * c[0];
3073   grad[0]        = PETSC_PI * (c[0] * c[1] - s[2] * s[0]);
3074   grad[1]        = PETSC_PI * (c[1] * c[2] - s[0] * s[1]);
3075   grad[2]        = PETSC_PI * (c[2] * c[0] - s[1] * s[2]);
3076   hess[0][0]     = -PetscSqr(PETSC_PI) * (s[0] * c[1] + s[2] * c[0]);
3077   hess[0][1]     = -PetscSqr(PETSC_PI) * (c[0] * s[1]);
3078   hess[0][2]     = -PetscSqr(PETSC_PI) * (c[2] * s[0]);
3079   hess[1][0]     = -PetscSqr(PETSC_PI) * (s[1] * c[2] + s[0] * c[1]);
3080   hess[1][1]     = -PetscSqr(PETSC_PI) * (c[1] * s[2]);
3081   hess[2][2]     = -PetscSqr(PETSC_PI) * (c[0] * s[1]);
3082   hess[2][0]     = -PetscSqr(PETSC_PI) * (s[2] * c[0] + s[1] * c[2]);
3083   hess[2][1]     = -PetscSqr(PETSC_PI) * (c[2] * s[0]);
3084   hess[2][2]     = -PetscSqr(PETSC_PI) * (c[1] * s[2]);
3085 }
3086 
3087 // u[] is a tentative normal on input. Replace with the implicit function gradient in the same direction
3088 static PetscErrorCode TPSExtrudeNormalFunc_Gyroid(PetscInt dim, PetscReal time, const PetscReal x[], PetscInt r, PetscScalar u[], void *ctx)
3089 {
3090   PetscReal s[3] = {PetscSinReal(PETSC_PI * x[0]), PetscSinReal(PETSC_PI * (x[1] + .5)), PetscSinReal(PETSC_PI * (x[2] + .25))};
3091   PetscReal c[3] = {PetscCosReal(PETSC_PI * x[0]), PetscCosReal(PETSC_PI * (x[1] + .5)), PetscCosReal(PETSC_PI * (x[2] + .25))};
3092   u[0]           = PETSC_PI * (c[0] * c[1] - s[2] * s[0]);
3093   u[1]           = PETSC_PI * (c[1] * c[2] - s[0] * s[1]);
3094   u[2]           = PETSC_PI * (c[2] * c[0] - s[1] * s[2]);
3095   return PETSC_SUCCESS;
3096 }
3097 
3098 /*
3099    We wish to solve
3100 
3101          min_y || y - x ||^2  subject to f(y) = 0
3102 
3103    Let g(y) = grad(f).  The minimization problem is equivalent to asking to satisfy
3104    f(y) = 0 and (y-x) is parallel to g(y).  We do this by using Householder QR to obtain a basis for the
3105    tangent space and ask for both components in the tangent space to be zero.
3106 
3107    Take g to be a column vector and compute the "full QR" factorization Q R = g,
3108    where Q = I - 2 n n^T is a symmetric orthogonal matrix.
3109    The first column of Q is parallel to g so the remaining two columns span the null space.
3110    Let Qn = Q[:,1:] be those remaining columns.  Then Qn Qn^T is an orthogonal projector into the tangent space.
3111    Since Q is symmetric, this is equivalent to multiplying by Q and taking the last two entries.
3112    In total, we have a system of 3 equations in 3 unknowns:
3113 
3114      f(y) = 0                       1 equation
3115      Qn^T (y - x) = 0               2 equations
3116 
3117    Here, we compute the residual and Jacobian of this system.
3118 */
3119 static void TPSNearestPointResJac(TPSEvaluateFunc feval, const PetscScalar x[], const PetscScalar y[], PetscScalar res[], PetscScalar J[])
3120 {
3121   PetscReal yreal[3] = {PetscRealPart(y[0]), PetscRealPart(y[1]), PetscRealPart(y[2])};
3122   PetscReal d[3]     = {PetscRealPart(y[0] - x[0]), PetscRealPart(y[1] - x[1]), PetscRealPart(y[2] - x[2])};
3123   PetscReal f, grad[3], n[3], norm, norm_y[3], nd, nd_y[3], sign;
3124   PetscReal n_y[3][3] = {
3125     {0, 0, 0},
3126     {0, 0, 0},
3127     {0, 0, 0}
3128   };
3129 
3130   feval(yreal, &f, grad, n_y);
3131 
3132   for (PetscInt i = 0; i < 3; i++) n[i] = grad[i];
3133   norm = PetscSqrtReal(PetscSqr(n[0]) + PetscSqr(n[1]) + PetscSqr(n[2]));
3134   for (PetscInt i = 0; i < 3; i++) norm_y[i] = 1. / norm * n[i] * n_y[i][i];
3135 
3136   // Define the Householder reflector
3137   sign = n[0] >= 0 ? 1. : -1.;
3138   n[0] += norm * sign;
3139   for (PetscInt i = 0; i < 3; i++) n_y[0][i] += norm_y[i] * sign;
3140 
3141   norm      = PetscSqrtReal(PetscSqr(n[0]) + PetscSqr(n[1]) + PetscSqr(n[2]));
3142   norm_y[0] = 1. / norm * (n[0] * n_y[0][0]);
3143   norm_y[1] = 1. / norm * (n[0] * n_y[0][1] + n[1] * n_y[1][1]);
3144   norm_y[2] = 1. / norm * (n[0] * n_y[0][2] + n[2] * n_y[2][2]);
3145 
3146   for (PetscInt i = 0; i < 3; i++) {
3147     n[i] /= norm;
3148     for (PetscInt j = 0; j < 3; j++) {
3149       // note that n[i] is n_old[i]/norm when executing the code below
3150       n_y[i][j] = n_y[i][j] / norm - n[i] / norm * norm_y[j];
3151     }
3152   }
3153 
3154   nd = n[0] * d[0] + n[1] * d[1] + n[2] * d[2];
3155   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];
3156 
3157   res[0] = f;
3158   res[1] = d[1] - 2 * n[1] * nd;
3159   res[2] = d[2] - 2 * n[2] * nd;
3160   // J[j][i] is J_{ij} (column major)
3161   for (PetscInt j = 0; j < 3; j++) {
3162     J[0 + j * 3] = grad[j];
3163     J[1 + j * 3] = (j == 1) * 1. - 2 * (n_y[1][j] * nd + n[1] * nd_y[j]);
3164     J[2 + j * 3] = (j == 2) * 1. - 2 * (n_y[2][j] * nd + n[2] * nd_y[j]);
3165   }
3166 }
3167 
3168 /*
3169    Project x to the nearest point on the implicit surface using Newton's method.
3170 */
3171 static PetscErrorCode TPSNearestPoint(TPSEvaluateFunc feval, PetscScalar x[])
3172 {
3173   PetscScalar y[3] = {x[0], x[1], x[2]}; // Initial guess
3174 
3175   PetscFunctionBegin;
3176   for (PetscInt iter = 0; iter < 10; iter++) {
3177     PetscScalar res[3], J[9];
3178     PetscReal   resnorm;
3179     TPSNearestPointResJac(feval, x, y, res, J);
3180     resnorm = PetscSqrtReal(PetscSqr(PetscRealPart(res[0])) + PetscSqr(PetscRealPart(res[1])) + PetscSqr(PetscRealPart(res[2])));
3181     if (0) { // Turn on this monitor if you need to confirm quadratic convergence
3182       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])));
3183     }
3184     if (resnorm < PETSC_SMALL) break;
3185 
3186     // Take the Newton step
3187     PetscCall(PetscKernel_A_gets_inverse_A_3(J, 0., PETSC_FALSE, NULL));
3188     PetscKernel_v_gets_v_minus_A_times_w_3(y, J, res);
3189   }
3190   for (PetscInt i = 0; i < 3; i++) x[i] = y[i];
3191   PetscFunctionReturn(PETSC_SUCCESS);
3192 }
3193 
3194 const char *const DMPlexTPSTypes[] = {"SCHWARZ_P", "GYROID", "DMPlexTPSType", "DMPLEX_TPS_", NULL};
3195 
3196 static PetscErrorCode DMPlexCreateTPSMesh_Internal(DM dm, DMPlexTPSType tpstype, const PetscInt extent[], const DMBoundaryType periodic[], PetscBool tps_distribute, PetscInt refinements, PetscInt layers, PetscReal thickness)
3197 {
3198   PetscMPIInt rank;
3199   PetscInt    topoDim = 2, spaceDim = 3, numFaces = 0, numVertices = 0, numEdges = 0;
3200   PetscInt(*edges)[2] = NULL, *edgeSets = NULL;
3201   PetscInt           *cells_flat = NULL;
3202   PetscReal          *vtxCoords  = NULL;
3203   TPSEvaluateFunc     evalFunc   = NULL;
3204   PetscSimplePointFn *normalFunc = NULL;
3205   DMLabel             label;
3206 
3207   PetscFunctionBegin;
3208   PetscCall(PetscLogEventBegin(DMPLEX_Generate, dm, 0, 0, 0));
3209   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
3210   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);
3211   switch (tpstype) {
3212   case DMPLEX_TPS_SCHWARZ_P:
3213     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");
3214     if (rank == 0) {
3215       PetscInt(*cells)[6][4][4] = NULL; // [junction, junction-face, cell, conn]
3216       PetscInt  Njunctions = 0, Ncuts = 0, Npipes[3], vcount;
3217       PetscReal L = 1;
3218 
3219       Npipes[0]   = (extent[0] + 1) * extent[1] * extent[2];
3220       Npipes[1]   = extent[0] * (extent[1] + 1) * extent[2];
3221       Npipes[2]   = extent[0] * extent[1] * (extent[2] + 1);
3222       Njunctions  = extent[0] * extent[1] * extent[2];
3223       Ncuts       = 2 * (extent[0] * extent[1] + extent[1] * extent[2] + extent[2] * extent[0]);
3224       numVertices = 4 * (Npipes[0] + Npipes[1] + Npipes[2]) + 8 * Njunctions;
3225       PetscCall(PetscMalloc1(3 * numVertices, &vtxCoords));
3226       PetscCall(PetscMalloc1(Njunctions, &cells));
3227       PetscCall(PetscMalloc1(Ncuts * 4, &edges));
3228       PetscCall(PetscMalloc1(Ncuts * 4, &edgeSets));
3229       // x-normal pipes
3230       vcount = 0;
3231       for (PetscInt i = 0; i < extent[0] + 1; i++) {
3232         for (PetscInt j = 0; j < extent[1]; j++) {
3233           for (PetscInt k = 0; k < extent[2]; k++) {
3234             for (PetscInt l = 0; l < 4; l++) {
3235               vtxCoords[vcount++] = (2 * i - 1) * L;
3236               vtxCoords[vcount++] = 2 * j * L + PetscCosReal((2 * l + 1) * PETSC_PI / 4) * L / 2;
3237               vtxCoords[vcount++] = 2 * k * L + PetscSinReal((2 * l + 1) * PETSC_PI / 4) * L / 2;
3238             }
3239           }
3240         }
3241       }
3242       // y-normal pipes
3243       for (PetscInt i = 0; i < extent[0]; i++) {
3244         for (PetscInt j = 0; j < extent[1] + 1; j++) {
3245           for (PetscInt k = 0; k < extent[2]; k++) {
3246             for (PetscInt l = 0; l < 4; l++) {
3247               vtxCoords[vcount++] = 2 * i * L + PetscSinReal((2 * l + 1) * PETSC_PI / 4) * L / 2;
3248               vtxCoords[vcount++] = (2 * j - 1) * L;
3249               vtxCoords[vcount++] = 2 * k * L + PetscCosReal((2 * l + 1) * PETSC_PI / 4) * L / 2;
3250             }
3251           }
3252         }
3253       }
3254       // z-normal pipes
3255       for (PetscInt i = 0; i < extent[0]; i++) {
3256         for (PetscInt j = 0; j < extent[1]; j++) {
3257           for (PetscInt k = 0; k < extent[2] + 1; k++) {
3258             for (PetscInt l = 0; l < 4; l++) {
3259               vtxCoords[vcount++] = 2 * i * L + PetscCosReal((2 * l + 1) * PETSC_PI / 4) * L / 2;
3260               vtxCoords[vcount++] = 2 * j * L + PetscSinReal((2 * l + 1) * PETSC_PI / 4) * L / 2;
3261               vtxCoords[vcount++] = (2 * k - 1) * L;
3262             }
3263           }
3264         }
3265       }
3266       // junctions
3267       for (PetscInt i = 0; i < extent[0]; i++) {
3268         for (PetscInt j = 0; j < extent[1]; j++) {
3269           for (PetscInt k = 0; k < extent[2]; k++) {
3270             const PetscInt J = (i * extent[1] + j) * extent[2] + k, Jvoff = (Npipes[0] + Npipes[1] + Npipes[2]) * 4 + J * 8;
3271             PetscCheck(vcount / 3 == Jvoff, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected vertex count");
3272             for (PetscInt ii = 0; ii < 2; ii++) {
3273               for (PetscInt jj = 0; jj < 2; jj++) {
3274                 for (PetscInt kk = 0; kk < 2; kk++) {
3275                   double Ls           = (1 - sqrt(2) / 4) * L;
3276                   vtxCoords[vcount++] = 2 * i * L + (2 * ii - 1) * Ls;
3277                   vtxCoords[vcount++] = 2 * j * L + (2 * jj - 1) * Ls;
3278                   vtxCoords[vcount++] = 2 * k * L + (2 * kk - 1) * Ls;
3279                 }
3280               }
3281             }
3282             const PetscInt jfaces[3][2][4] = {
3283               {{3, 1, 0, 2}, {7, 5, 4, 6}}, // x-aligned
3284               {{5, 4, 0, 1}, {7, 6, 2, 3}}, // y-aligned
3285               {{6, 2, 0, 4}, {7, 3, 1, 5}}  // z-aligned
3286             };
3287             const PetscInt pipe_lo[3] = {// vertex numbers of pipes
3288                                          ((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};
3289             const PetscInt pipe_hi[3] = {// vertex numbers of pipes
3290                                          (((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};
3291             for (PetscInt dir = 0; dir < 3; dir++) { // x,y,z
3292               const PetscInt ijk[3] = {i, j, k};
3293               for (PetscInt l = 0; l < 4; l++) { // rotations
3294                 cells[J][dir * 2 + 0][l][0] = pipe_lo[dir] + l;
3295                 cells[J][dir * 2 + 0][l][1] = Jvoff + jfaces[dir][0][l];
3296                 cells[J][dir * 2 + 0][l][2] = Jvoff + jfaces[dir][0][(l - 1 + 4) % 4];
3297                 cells[J][dir * 2 + 0][l][3] = pipe_lo[dir] + (l - 1 + 4) % 4;
3298                 cells[J][dir * 2 + 1][l][0] = Jvoff + jfaces[dir][1][l];
3299                 cells[J][dir * 2 + 1][l][1] = pipe_hi[dir] + l;
3300                 cells[J][dir * 2 + 1][l][2] = pipe_hi[dir] + (l - 1 + 4) % 4;
3301                 cells[J][dir * 2 + 1][l][3] = Jvoff + jfaces[dir][1][(l - 1 + 4) % 4];
3302                 if (ijk[dir] == 0) {
3303                   edges[numEdges][0] = pipe_lo[dir] + l;
3304                   edges[numEdges][1] = pipe_lo[dir] + (l + 1) % 4;
3305                   edgeSets[numEdges] = dir * 2 + 1;
3306                   numEdges++;
3307                 }
3308                 if (ijk[dir] + 1 == extent[dir]) {
3309                   edges[numEdges][0] = pipe_hi[dir] + l;
3310                   edges[numEdges][1] = pipe_hi[dir] + (l + 1) % 4;
3311                   edgeSets[numEdges] = dir * 2 + 2;
3312                   numEdges++;
3313                 }
3314               }
3315             }
3316           }
3317         }
3318       }
3319       PetscCheck(numEdges == Ncuts * 4, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Edge count %" PetscInt_FMT " incompatible with number of cuts %" PetscInt_FMT, numEdges, Ncuts);
3320       numFaces   = 24 * Njunctions;
3321       cells_flat = cells[0][0][0];
3322     }
3323     evalFunc   = TPSEvaluate_SchwarzP;
3324     normalFunc = TPSExtrudeNormalFunc_SchwarzP;
3325     break;
3326   case DMPLEX_TPS_GYROID:
3327     if (rank == 0) {
3328       // This is a coarse mesh approximation of the gyroid shifted to being the zero of the level set
3329       //
3330       //     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)
3331       //
3332       // on the cell [0,2]^3.
3333       //
3334       // Think about dividing that cell into four columns, and focus on the column [0,1]x[0,1]x[0,2].
3335       // If you looked at the gyroid in that column at different slices of z you would see that it kind of spins
3336       // like a boomerang:
3337       //
3338       //     z = 0          z = 1/4        z = 1/2        z = 3/4     //
3339       //     -----          -------        -------        -------     //
3340       //                                                              //
3341       //     +       +      +       +      +       +      +   \   +   //
3342       //      \                                   /            \      //
3343       //       \            `-_   _-'            /              }     //
3344       //        *-_            `-'            _-'              /      //
3345       //     +     `-+      +       +      +-'     +      +   /   +   //
3346       //                                                              //
3347       //                                                              //
3348       //     z = 1          z = 5/4        z = 3/2        z = 7/4     //
3349       //     -----          -------        -------        -------     //
3350       //                                                              //
3351       //     +-_     +      +       +      +     _-+      +   /   +   //
3352       //        `-_            _-_            _-`            /        //
3353       //           \        _-'   `-_        /              {         //
3354       //            \                       /                \        //
3355       //     +       +      +       +      +       +      +   \   +   //
3356       //
3357       //
3358       // This course mesh approximates each of these slices by two line segments,
3359       // and then connects the segments in consecutive layers with quadrilateral faces.
3360       // All of the end points of the segments are multiples of 1/4 except for the
3361       // point * in the picture for z = 0 above and the similar points in other layers.
3362       // That point is at (gamma, gamma, 0), where gamma is calculated below.
3363       //
3364       // The column  [1,2]x[1,2]x[0,2] looks the same as this column;
3365       // The columns [1,2]x[0,1]x[0,2] and [0,1]x[1,2]x[0,2] are mirror images.
3366       //
3367       // As for how this method turned into the names given to the vertices:
3368       // that was not systematic, it was just the way it worked out in my handwritten notes.
3369 
3370       PetscInt facesPerBlock = 64;
3371       PetscInt vertsPerBlock = 56;
3372       PetscInt extentPlus[3];
3373       PetscInt numBlocks, numBlocksPlus;
3374       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;
3375       const PetscInt pattern[64][4] = {
3376         /* face to vertex within the coarse discretization of a single gyroid block */
3377         /* layer 0 */
3378         {A,           C,           K,           G          },
3379         {C,           B,           II,          K          },
3380         {D,           A,           H,           L          },
3381         {B + 56 * 1,  D,           L,           J          },
3382         {E,           B + 56 * 1,  J,           N          },
3383         {A + 56 * 2,  E,           N,           H + 56 * 2 },
3384         {F,           A + 56 * 2,  G + 56 * 2,  M          },
3385         {B,           F,           M,           II         },
3386         /* layer 1 */
3387         {G,           K,           Q,           O          },
3388         {K,           II,          P,           Q          },
3389         {L,           H,           O + 56 * 1,  R          },
3390         {J,           L,           R,           P          },
3391         {N,           J,           P,           S          },
3392         {H + 56 * 2,  N,           S,           O + 56 * 3 },
3393         {M,           G + 56 * 2,  O + 56 * 2,  T          },
3394         {II,          M,           T,           P          },
3395         /* layer 2 */
3396         {O,           Q,           Y,           U          },
3397         {Q,           P,           W,           Y          },
3398         {R,           O + 56 * 1,  U + 56 * 1,  Ap         },
3399         {P,           R,           Ap,          W          },
3400         {S,           P,           X,           Bp         },
3401         {O + 56 * 3,  S,           Bp,          V + 56 * 1 },
3402         {T,           O + 56 * 2,  V,           Z          },
3403         {P,           T,           Z,           X          },
3404         /* layer 3 */
3405         {U,           Y,           Ep,          Dp         },
3406         {Y,           W,           Cp,          Ep         },
3407         {Ap,          U + 56 * 1,  Dp + 56 * 1, Gp         },
3408         {W,           Ap,          Gp,          Cp         },
3409         {Bp,          X,           Cp + 56 * 2, Fp         },
3410         {V + 56 * 1,  Bp,          Fp,          Dp + 56 * 1},
3411         {Z,           V,           Dp,          Hp         },
3412         {X,           Z,           Hp,          Cp + 56 * 2},
3413         /* layer 4 */
3414         {Dp,          Ep,          Mp,          Kp         },
3415         {Ep,          Cp,          Ip,          Mp         },
3416         {Gp,          Dp + 56 * 1, Lp,          Np         },
3417         {Cp,          Gp,          Np,          Jp         },
3418         {Fp,          Cp + 56 * 2, Jp + 56 * 2, Pp         },
3419         {Dp + 56 * 1, Fp,          Pp,          Lp         },
3420         {Hp,          Dp,          Kp,          Op         },
3421         {Cp + 56 * 2, Hp,          Op,          Ip + 56 * 2},
3422         /* layer 5 */
3423         {Kp,          Mp,          Sp,          Rp         },
3424         {Mp,          Ip,          Qp,          Sp         },
3425         {Np,          Lp,          Rp,          Tp         },
3426         {Jp,          Np,          Tp,          Qp + 56 * 1},
3427         {Pp,          Jp + 56 * 2, Qp + 56 * 3, Up         },
3428         {Lp,          Pp,          Up,          Rp         },
3429         {Op,          Kp,          Rp,          Vp         },
3430         {Ip + 56 * 2, Op,          Vp,          Qp + 56 * 2},
3431         /* layer 6 */
3432         {Rp,          Sp,          Aq,          Yp         },
3433         {Sp,          Qp,          Wp,          Aq         },
3434         {Tp,          Rp,          Yp,          Cq         },
3435         {Qp + 56 * 1, Tp,          Cq,          Wp + 56 * 1},
3436         {Up,          Qp + 56 * 3, Xp + 56 * 1, Dq         },
3437         {Rp,          Up,          Dq,          Zp         },
3438         {Vp,          Rp,          Zp,          Bq         },
3439         {Qp + 56 * 2, Vp,          Bq,          Xp         },
3440         /* layer 7 (the top is the periodic image of the bottom of layer 0) */
3441         {Yp,          Aq,          C + 56 * 4,  A + 56 * 4 },
3442         {Aq,          Wp,          B + 56 * 4,  C + 56 * 4 },
3443         {Cq,          Yp,          A + 56 * 4,  D + 56 * 4 },
3444         {Wp + 56 * 1, Cq,          D + 56 * 4,  B + 56 * 5 },
3445         {Dq,          Xp + 56 * 1, B + 56 * 5,  E + 56 * 4 },
3446         {Zp,          Dq,          E + 56 * 4,  A + 56 * 6 },
3447         {Bq,          Zp,          A + 56 * 6,  F + 56 * 4 },
3448         {Xp,          Bq,          F + 56 * 4,  B + 56 * 4 }
3449       };
3450       const PetscReal gamma                = PetscAcosReal((PetscSqrtReal(3.) - 1.) / PetscSqrtReal(2.)) / PETSC_PI;
3451       const PetscReal patternCoords[56][3] = {
3452         {1.,        0.,        0.  }, /* A  */
3453         {0.,        1.,        0.  }, /* B  */
3454         {gamma,     gamma,     0.  }, /* C  */
3455         {1 + gamma, 1 - gamma, 0.  }, /* D  */
3456         {2 - gamma, 2 - gamma, 0.  }, /* E  */
3457         {1 - gamma, 1 + gamma, 0.  }, /* F  */
3458 
3459         {.5,        0,         .25 }, /* G  */
3460         {1.5,       0.,        .25 }, /* H  */
3461         {.5,        1.,        .25 }, /* II */
3462         {1.5,       1.,        .25 }, /* J  */
3463         {.25,       .5,        .25 }, /* K  */
3464         {1.25,      .5,        .25 }, /* L  */
3465         {.75,       1.5,       .25 }, /* M  */
3466         {1.75,      1.5,       .25 }, /* N  */
3467 
3468         {0.,        0.,        .5  }, /* O  */
3469         {1.,        1.,        .5  }, /* P  */
3470         {gamma,     1 - gamma, .5  }, /* Q  */
3471         {1 + gamma, gamma,     .5  }, /* R  */
3472         {2 - gamma, 1 + gamma, .5  }, /* S  */
3473         {1 - gamma, 2 - gamma, .5  }, /* T  */
3474 
3475         {0.,        .5,        .75 }, /* U  */
3476         {0.,        1.5,       .75 }, /* V  */
3477         {1.,        .5,        .75 }, /* W  */
3478         {1.,        1.5,       .75 }, /* X  */
3479         {.5,        .75,       .75 }, /* Y  */
3480         {.5,        1.75,      .75 }, /* Z  */
3481         {1.5,       .25,       .75 }, /* Ap */
3482         {1.5,       1.25,      .75 }, /* Bp */
3483 
3484         {1.,        0.,        1.  }, /* Cp */
3485         {0.,        1.,        1.  }, /* Dp */
3486         {1 - gamma, 1 - gamma, 1.  }, /* Ep */
3487         {1 + gamma, 1 + gamma, 1.  }, /* Fp */
3488         {2 - gamma, gamma,     1.  }, /* Gp */
3489         {gamma,     2 - gamma, 1.  }, /* Hp */
3490 
3491         {.5,        0.,        1.25}, /* Ip */
3492         {1.5,       0.,        1.25}, /* Jp */
3493         {.5,        1.,        1.25}, /* Kp */
3494         {1.5,       1.,        1.25}, /* Lp */
3495         {.75,       .5,        1.25}, /* Mp */
3496         {1.75,      .5,        1.25}, /* Np */
3497         {.25,       1.5,       1.25}, /* Op */
3498         {1.25,      1.5,       1.25}, /* Pp */
3499 
3500         {0.,        0.,        1.5 }, /* Qp */
3501         {1.,        1.,        1.5 }, /* Rp */
3502         {1 - gamma, gamma,     1.5 }, /* Sp */
3503         {2 - gamma, 1 - gamma, 1.5 }, /* Tp */
3504         {1 + gamma, 2 - gamma, 1.5 }, /* Up */
3505         {gamma,     1 + gamma, 1.5 }, /* Vp */
3506 
3507         {0.,        .5,        1.75}, /* Wp */
3508         {0.,        1.5,       1.75}, /* Xp */
3509         {1.,        .5,        1.75}, /* Yp */
3510         {1.,        1.5,       1.75}, /* Zp */
3511         {.5,        .25,       1.75}, /* Aq */
3512         {.5,        1.25,      1.75}, /* Bq */
3513         {1.5,       .75,       1.75}, /* Cq */
3514         {1.5,       1.75,      1.75}, /* Dq */
3515       };
3516       PetscInt(*cells)[64][4] = NULL;
3517       PetscBool *seen;
3518       PetscInt  *vertToTrueVert;
3519       PetscInt   count;
3520 
3521       for (PetscInt i = 0; i < 3; i++) extentPlus[i] = extent[i] + 1;
3522       numBlocks = 1;
3523       for (PetscInt i = 0; i < 3; i++) numBlocks *= extent[i];
3524       numBlocksPlus = 1;
3525       for (PetscInt i = 0; i < 3; i++) numBlocksPlus *= extentPlus[i];
3526       numFaces = numBlocks * facesPerBlock;
3527       PetscCall(PetscMalloc1(numBlocks, &cells));
3528       PetscCall(PetscCalloc1(numBlocksPlus * vertsPerBlock, &seen));
3529       for (PetscInt k = 0; k < extent[2]; k++) {
3530         for (PetscInt j = 0; j < extent[1]; j++) {
3531           for (PetscInt i = 0; i < extent[0]; i++) {
3532             for (PetscInt f = 0; f < facesPerBlock; f++) {
3533               for (PetscInt v = 0; v < 4; v++) {
3534                 PetscInt vertRaw     = pattern[f][v];
3535                 PetscInt blockidx    = vertRaw / 56;
3536                 PetscInt patternvert = vertRaw % 56;
3537                 PetscInt xplus       = (blockidx & 1);
3538                 PetscInt yplus       = (blockidx & 2) >> 1;
3539                 PetscInt zplus       = (blockidx & 4) >> 2;
3540                 PetscInt zcoord      = (periodic && periodic[2] == DM_BOUNDARY_PERIODIC) ? ((k + zplus) % extent[2]) : (k + zplus);
3541                 PetscInt ycoord      = (periodic && periodic[1] == DM_BOUNDARY_PERIODIC) ? ((j + yplus) % extent[1]) : (j + yplus);
3542                 PetscInt xcoord      = (periodic && periodic[0] == DM_BOUNDARY_PERIODIC) ? ((i + xplus) % extent[0]) : (i + xplus);
3543                 PetscInt vert        = ((zcoord * extentPlus[1] + ycoord) * extentPlus[0] + xcoord) * 56 + patternvert;
3544 
3545                 cells[(k * extent[1] + j) * extent[0] + i][f][v] = vert;
3546                 seen[vert]                                       = PETSC_TRUE;
3547               }
3548             }
3549           }
3550         }
3551       }
3552       for (PetscInt i = 0; i < numBlocksPlus * vertsPerBlock; i++)
3553         if (seen[i]) numVertices++;
3554       count = 0;
3555       PetscCall(PetscMalloc1(numBlocksPlus * vertsPerBlock, &vertToTrueVert));
3556       PetscCall(PetscMalloc1(numVertices * 3, &vtxCoords));
3557       for (PetscInt i = 0; i < numBlocksPlus * vertsPerBlock; i++) vertToTrueVert[i] = -1;
3558       for (PetscInt k = 0; k < extentPlus[2]; k++) {
3559         for (PetscInt j = 0; j < extentPlus[1]; j++) {
3560           for (PetscInt i = 0; i < extentPlus[0]; i++) {
3561             for (PetscInt v = 0; v < vertsPerBlock; v++) {
3562               PetscInt vIdx = ((k * extentPlus[1] + j) * extentPlus[0] + i) * vertsPerBlock + v;
3563 
3564               if (seen[vIdx]) {
3565                 PetscInt thisVert;
3566 
3567                 vertToTrueVert[vIdx] = thisVert = count++;
3568 
3569                 for (PetscInt d = 0; d < 3; d++) vtxCoords[3 * thisVert + d] = patternCoords[v][d];
3570                 vtxCoords[3 * thisVert + 0] += i * 2;
3571                 vtxCoords[3 * thisVert + 1] += j * 2;
3572                 vtxCoords[3 * thisVert + 2] += k * 2;
3573               }
3574             }
3575           }
3576         }
3577       }
3578       for (PetscInt i = 0; i < numBlocks; i++) {
3579         for (PetscInt f = 0; f < facesPerBlock; f++) {
3580           for (PetscInt v = 0; v < 4; v++) cells[i][f][v] = vertToTrueVert[cells[i][f][v]];
3581         }
3582       }
3583       PetscCall(PetscFree(vertToTrueVert));
3584       PetscCall(PetscFree(seen));
3585       cells_flat = cells[0][0];
3586       numEdges   = 0;
3587       for (PetscInt i = 0; i < numFaces; i++) {
3588         for (PetscInt e = 0; e < 4; e++) {
3589           PetscInt         ev[]       = {cells_flat[i * 4 + e], cells_flat[i * 4 + ((e + 1) % 4)]};
3590           const PetscReal *evCoords[] = {&vtxCoords[3 * ev[0]], &vtxCoords[3 * ev[1]]};
3591 
3592           for (PetscInt d = 0; d < 3; d++) {
3593             if (!periodic || periodic[0] != DM_BOUNDARY_PERIODIC) {
3594               if (evCoords[0][d] == 0. && evCoords[1][d] == 0.) numEdges++;
3595               if (evCoords[0][d] == 2. * extent[d] && evCoords[1][d] == 2. * extent[d]) numEdges++;
3596             }
3597           }
3598         }
3599       }
3600       PetscCall(PetscMalloc1(numEdges, &edges));
3601       PetscCall(PetscMalloc1(numEdges, &edgeSets));
3602       for (PetscInt edge = 0, i = 0; i < numFaces; i++) {
3603         for (PetscInt e = 0; e < 4; e++) {
3604           PetscInt         ev[]       = {cells_flat[i * 4 + e], cells_flat[i * 4 + ((e + 1) % 4)]};
3605           const PetscReal *evCoords[] = {&vtxCoords[3 * ev[0]], &vtxCoords[3 * ev[1]]};
3606 
3607           for (PetscInt d = 0; d < 3; d++) {
3608             if (!periodic || periodic[d] != DM_BOUNDARY_PERIODIC) {
3609               if (evCoords[0][d] == 0. && evCoords[1][d] == 0.) {
3610                 edges[edge][0]   = ev[0];
3611                 edges[edge][1]   = ev[1];
3612                 edgeSets[edge++] = 2 * d;
3613               }
3614               if (evCoords[0][d] == 2. * extent[d] && evCoords[1][d] == 2. * extent[d]) {
3615                 edges[edge][0]   = ev[0];
3616                 edges[edge][1]   = ev[1];
3617                 edgeSets[edge++] = 2 * d + 1;
3618               }
3619             }
3620           }
3621         }
3622       }
3623     }
3624     evalFunc   = TPSEvaluate_Gyroid;
3625     normalFunc = TPSExtrudeNormalFunc_Gyroid;
3626     break;
3627   }
3628 
3629   PetscCall(DMSetDimension(dm, topoDim));
3630   if (rank == 0) PetscCall(DMPlexBuildFromCellList(dm, numFaces, numVertices, 4, cells_flat));
3631   else PetscCall(DMPlexBuildFromCellList(dm, 0, 0, 0, NULL));
3632   PetscCall(PetscFree(cells_flat));
3633   {
3634     DM idm;
3635     PetscCall(DMPlexInterpolate(dm, &idm));
3636     PetscCall(DMPlexReplace_Internal(dm, &idm));
3637   }
3638   if (rank == 0) PetscCall(DMPlexBuildCoordinatesFromCellList(dm, spaceDim, vtxCoords));
3639   else PetscCall(DMPlexBuildCoordinatesFromCellList(dm, spaceDim, NULL));
3640   PetscCall(PetscFree(vtxCoords));
3641 
3642   PetscCall(DMCreateLabel(dm, "Face Sets"));
3643   PetscCall(DMGetLabel(dm, "Face Sets", &label));
3644   for (PetscInt e = 0; e < numEdges; e++) {
3645     PetscInt        njoin;
3646     const PetscInt *join, verts[] = {numFaces + edges[e][0], numFaces + edges[e][1]};
3647     PetscCall(DMPlexGetJoin(dm, 2, verts, &njoin, &join));
3648     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]);
3649     PetscCall(DMLabelSetValue(label, join[0], edgeSets[e]));
3650     PetscCall(DMPlexRestoreJoin(dm, 2, verts, &njoin, &join));
3651   }
3652   PetscCall(PetscFree(edges));
3653   PetscCall(PetscFree(edgeSets));
3654   if (tps_distribute) {
3655     DM               pdm = NULL;
3656     PetscPartitioner part;
3657 
3658     PetscCall(DMPlexGetPartitioner(dm, &part));
3659     PetscCall(PetscPartitionerSetFromOptions(part));
3660     PetscCall(DMPlexDistribute(dm, 0, NULL, &pdm));
3661     if (pdm) PetscCall(DMPlexReplace_Internal(dm, &pdm));
3662     // Do not auto-distribute again
3663     PetscCall(DMPlexDistributeSetDefault(dm, PETSC_FALSE));
3664   }
3665 
3666   PetscCall(DMPlexSetRefinementUniform(dm, PETSC_TRUE));
3667   for (PetscInt refine = 0; refine < refinements; refine++) {
3668     PetscInt     m;
3669     DM           dmf;
3670     Vec          X;
3671     PetscScalar *x;
3672     PetscCall(DMRefine(dm, MPI_COMM_NULL, &dmf));
3673     PetscCall(DMPlexReplace_Internal(dm, &dmf));
3674 
3675     PetscCall(DMGetCoordinatesLocal(dm, &X));
3676     PetscCall(VecGetLocalSize(X, &m));
3677     PetscCall(VecGetArray(X, &x));
3678     for (PetscInt i = 0; i < m; i += 3) PetscCall(TPSNearestPoint(evalFunc, &x[i]));
3679     PetscCall(VecRestoreArray(X, &x));
3680   }
3681 
3682   // Face Sets has already been propagated to new vertices during refinement; this propagates to the initial vertices.
3683   PetscCall(DMGetLabel(dm, "Face Sets", &label));
3684   PetscCall(DMPlexLabelComplete(dm, label));
3685 
3686   PetscCall(PetscLogEventEnd(DMPLEX_Generate, dm, 0, 0, 0));
3687 
3688   if (thickness > 0) {
3689     DM              edm, cdm, ecdm;
3690     DMPlexTransform tr;
3691     const char     *prefix;
3692     PetscOptions    options;
3693     // Code from DMPlexExtrude
3694     PetscCall(DMPlexTransformCreate(PetscObjectComm((PetscObject)dm), &tr));
3695     PetscCall(DMPlexTransformSetDM(tr, dm));
3696     PetscCall(DMPlexTransformSetType(tr, DMPLEXEXTRUDE));
3697     PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
3698     PetscCall(PetscObjectSetOptionsPrefix((PetscObject)tr, prefix));
3699     PetscCall(PetscObjectGetOptions((PetscObject)dm, &options));
3700     PetscCall(PetscObjectSetOptions((PetscObject)tr, options));
3701     PetscCall(DMPlexTransformExtrudeSetLayers(tr, layers));
3702     PetscCall(DMPlexTransformExtrudeSetThickness(tr, thickness));
3703     PetscCall(DMPlexTransformExtrudeSetTensor(tr, PETSC_FALSE));
3704     PetscCall(DMPlexTransformExtrudeSetSymmetric(tr, PETSC_TRUE));
3705     PetscCall(DMPlexTransformExtrudeSetNormalFunction(tr, normalFunc));
3706     PetscCall(DMPlexTransformSetFromOptions(tr));
3707     PetscCall(PetscObjectSetOptions((PetscObject)tr, NULL));
3708     PetscCall(DMPlexTransformSetUp(tr));
3709     PetscCall(PetscObjectViewFromOptions((PetscObject)tr, NULL, "-dm_plex_tps_transform_view"));
3710     PetscCall(DMPlexTransformApply(tr, dm, &edm));
3711     PetscCall(DMCopyDisc(dm, edm));
3712     PetscCall(DMGetCoordinateDM(dm, &cdm));
3713     PetscCall(DMGetCoordinateDM(edm, &ecdm));
3714     PetscCall(DMCopyDisc(cdm, ecdm));
3715     PetscCall(DMPlexTransformCreateDiscLabels(tr, edm));
3716     PetscCall(DMPlexTransformDestroy(&tr));
3717     PetscCall(DMPlexCopy_Internal(dm, PETSC_FALSE, PETSC_FALSE, edm));
3718     PetscCall(DMPlexReplace_Internal(dm, &edm));
3719   }
3720   PetscFunctionReturn(PETSC_SUCCESS);
3721 }
3722 
3723 /*@
3724   DMPlexCreateTPSMesh - Create a distributed, interpolated mesh of a triply-periodic surface
3725 
3726   Collective
3727 
3728   Input Parameters:
3729 + comm           - The communicator for the `DM` object
3730 . tpstype        - Type of triply-periodic surface
3731 . extent         - Array of length 3 containing number of periods in each direction
3732 . periodic       - array of length 3 with periodicity, or `NULL` for non-periodic
3733 . tps_distribute - Distribute 2D manifold mesh prior to refinement and extrusion (more scalable)
3734 . refinements    - Number of factor-of-2 refinements of 2D manifold mesh
3735 . layers         - Number of cell layers extruded in normal direction
3736 - thickness      - Thickness in normal direction
3737 
3738   Output Parameter:
3739 . dm - The `DM` object
3740 
3741   Level: beginner
3742 
3743   Notes:
3744   This meshes the surface of the Schwarz P or Gyroid surfaces.  Schwarz P is the simplest member of the triply-periodic minimal surfaces.
3745   <https://en.wikipedia.org/wiki/Schwarz_minimal_surface#Schwarz_P_(%22Primitive%22)> and can be cut with "clean" boundaries.
3746   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.
3747   Our implementation creates a very coarse mesh of the surface and refines (by 4-way splitting) as many times as requested.
3748   On each refinement, all vertices are projected to their nearest point on the surface.
3749   This projection could readily be extended to related surfaces.
3750 
3751   See {cite}`maskery2018insights`
3752 
3753   The face (edge) sets for the Schwarz P surface are numbered $1(-x), 2(+x), 3(-y), 4(+y), 5(-z), 6(+z)$.
3754   When the mesh is refined, "Face Sets" contain the new vertices (created during refinement).
3755   Use `DMPlexLabelComplete()` to propagate to coarse-level vertices.
3756 
3757   Developer Notes:
3758   The Gyroid mesh does not currently mark boundary sets.
3759 
3760 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateSphereMesh()`, `DMSetType()`, `DMCreate()`
3761 @*/
3762 PetscErrorCode DMPlexCreateTPSMesh(MPI_Comm comm, DMPlexTPSType tpstype, const PetscInt extent[], const DMBoundaryType periodic[], PetscBool tps_distribute, PetscInt refinements, PetscInt layers, PetscReal thickness, DM *dm)
3763 {
3764   PetscFunctionBegin;
3765   PetscCall(DMCreate(comm, dm));
3766   PetscCall(DMSetType(*dm, DMPLEX));
3767   PetscCall(DMPlexCreateTPSMesh_Internal(*dm, tpstype, extent, periodic, tps_distribute, refinements, layers, thickness));
3768   PetscFunctionReturn(PETSC_SUCCESS);
3769 }
3770 
3771 /*@
3772   DMPlexCreateSphereMesh - Creates a mesh on the d-dimensional sphere, S^d.
3773 
3774   Collective
3775 
3776   Input Parameters:
3777 + comm    - The communicator for the `DM` object
3778 . dim     - The dimension
3779 . simplex - Use simplices, or tensor product cells
3780 - R       - The radius
3781 
3782   Output Parameter:
3783 . dm - The `DM` object
3784 
3785   Level: beginner
3786 
3787 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateBallMesh()`, `DMPlexCreateBoxMesh()`, `DMSetType()`, `DMCreate()`
3788 @*/
3789 PetscErrorCode DMPlexCreateSphereMesh(MPI_Comm comm, PetscInt dim, PetscBool simplex, PetscReal R, DM *dm)
3790 {
3791   PetscFunctionBegin;
3792   PetscAssertPointer(dm, 5);
3793   PetscCall(DMCreate(comm, dm));
3794   PetscCall(DMSetType(*dm, DMPLEX));
3795   PetscCall(DMPlexCreateSphereMesh_Internal(*dm, dim, simplex, R));
3796   PetscFunctionReturn(PETSC_SUCCESS);
3797 }
3798 
3799 static PetscErrorCode DMPlexCreateBallMesh_Internal(DM dm, PetscInt dim, PetscReal R)
3800 {
3801   DM          sdm, vol;
3802   DMLabel     bdlabel;
3803   const char *prefix;
3804 
3805   PetscFunctionBegin;
3806   PetscCall(DMCreate(PetscObjectComm((PetscObject)dm), &sdm));
3807   PetscCall(DMSetType(sdm, DMPLEX));
3808   PetscCall(DMGetOptionsPrefix(dm, &prefix));
3809   PetscCall(DMSetOptionsPrefix(sdm, prefix));
3810   PetscCall(DMAppendOptionsPrefix(sdm, "bd_"));
3811   PetscCall(DMPlexDistributeSetDefault(sdm, PETSC_FALSE));
3812   PetscCall(DMPlexCreateSphereMesh_Internal(sdm, dim - 1, PETSC_TRUE, R));
3813   PetscCall(DMSetFromOptions(sdm));
3814   PetscCall(DMViewFromOptions(sdm, NULL, "-dm_view"));
3815   PetscCall(DMPlexGenerate(sdm, NULL, PETSC_TRUE, &vol));
3816   PetscCall(DMDestroy(&sdm));
3817   PetscCall(DMPlexReplace_Internal(dm, &vol));
3818   PetscCall(DMCreateLabel(dm, "marker"));
3819   PetscCall(DMGetLabel(dm, "marker", &bdlabel));
3820   PetscCall(DMPlexMarkBoundaryFaces(dm, PETSC_DETERMINE, bdlabel));
3821   PetscCall(DMPlexLabelComplete(dm, bdlabel));
3822   PetscFunctionReturn(PETSC_SUCCESS);
3823 }
3824 
3825 /*@
3826   DMPlexCreateBallMesh - Creates a simplex mesh on the d-dimensional ball, B^d.
3827 
3828   Collective
3829 
3830   Input Parameters:
3831 + comm - The communicator for the `DM` object
3832 . dim  - The dimension
3833 - R    - The radius
3834 
3835   Output Parameter:
3836 . dm - The `DM` object
3837 
3838   Options Database Key:
3839 . bd_dm_refine - This will refine the surface mesh preserving the sphere geometry
3840 
3841   Level: beginner
3842 
3843 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateSphereMesh()`, `DMPlexCreateBoxMesh()`, `DMSetType()`, `DMCreate()`
3844 @*/
3845 PetscErrorCode DMPlexCreateBallMesh(MPI_Comm comm, PetscInt dim, PetscReal R, DM *dm)
3846 {
3847   PetscFunctionBegin;
3848   PetscCall(DMCreate(comm, dm));
3849   PetscCall(DMSetType(*dm, DMPLEX));
3850   PetscCall(DMPlexCreateBallMesh_Internal(*dm, dim, R));
3851   PetscFunctionReturn(PETSC_SUCCESS);
3852 }
3853 
3854 static PetscErrorCode DMPlexCreateReferenceCell_Internal(DM rdm, DMPolytopeType ct)
3855 {
3856   PetscFunctionBegin;
3857   switch (ct) {
3858   case DM_POLYTOPE_POINT: {
3859     PetscInt    numPoints[1]        = {1};
3860     PetscInt    coneSize[1]         = {0};
3861     PetscInt    cones[1]            = {0};
3862     PetscInt    coneOrientations[1] = {0};
3863     PetscScalar vertexCoords[1]     = {0.0};
3864 
3865     PetscCall(DMSetDimension(rdm, 0));
3866     PetscCall(DMPlexCreateFromDAG(rdm, 0, numPoints, coneSize, cones, coneOrientations, vertexCoords));
3867   } break;
3868   case DM_POLYTOPE_SEGMENT: {
3869     PetscInt    numPoints[2]        = {2, 1};
3870     PetscInt    coneSize[3]         = {2, 0, 0};
3871     PetscInt    cones[2]            = {1, 2};
3872     PetscInt    coneOrientations[2] = {0, 0};
3873     PetscScalar vertexCoords[2]     = {-1.0, 1.0};
3874 
3875     PetscCall(DMSetDimension(rdm, 1));
3876     PetscCall(DMPlexCreateFromDAG(rdm, 1, numPoints, coneSize, cones, coneOrientations, vertexCoords));
3877   } break;
3878   case DM_POLYTOPE_POINT_PRISM_TENSOR: {
3879     PetscInt    numPoints[2]        = {2, 1};
3880     PetscInt    coneSize[3]         = {2, 0, 0};
3881     PetscInt    cones[2]            = {1, 2};
3882     PetscInt    coneOrientations[2] = {0, 0};
3883     PetscScalar vertexCoords[2]     = {-1.0, 1.0};
3884 
3885     PetscCall(DMSetDimension(rdm, 1));
3886     PetscCall(DMPlexCreateFromDAG(rdm, 1, numPoints, coneSize, cones, coneOrientations, vertexCoords));
3887   } break;
3888   case DM_POLYTOPE_TRIANGLE: {
3889     PetscInt    numPoints[2]        = {3, 1};
3890     PetscInt    coneSize[4]         = {3, 0, 0, 0};
3891     PetscInt    cones[3]            = {1, 2, 3};
3892     PetscInt    coneOrientations[3] = {0, 0, 0};
3893     PetscScalar vertexCoords[6]     = {-1.0, -1.0, 1.0, -1.0, -1.0, 1.0};
3894 
3895     PetscCall(DMSetDimension(rdm, 2));
3896     PetscCall(DMPlexCreateFromDAG(rdm, 1, numPoints, coneSize, cones, coneOrientations, vertexCoords));
3897   } break;
3898   case DM_POLYTOPE_QUADRILATERAL: {
3899     PetscInt    numPoints[2]        = {4, 1};
3900     PetscInt    coneSize[5]         = {4, 0, 0, 0, 0};
3901     PetscInt    cones[4]            = {1, 2, 3, 4};
3902     PetscInt    coneOrientations[4] = {0, 0, 0, 0};
3903     PetscScalar vertexCoords[8]     = {-1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0};
3904 
3905     PetscCall(DMSetDimension(rdm, 2));
3906     PetscCall(DMPlexCreateFromDAG(rdm, 1, numPoints, coneSize, cones, coneOrientations, vertexCoords));
3907   } break;
3908   case DM_POLYTOPE_SEG_PRISM_TENSOR: {
3909     PetscInt    numPoints[2]        = {4, 1};
3910     PetscInt    coneSize[5]         = {4, 0, 0, 0, 0};
3911     PetscInt    cones[4]            = {1, 2, 3, 4};
3912     PetscInt    coneOrientations[4] = {0, 0, 0, 0};
3913     PetscScalar vertexCoords[8]     = {-1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0};
3914 
3915     PetscCall(DMSetDimension(rdm, 2));
3916     PetscCall(DMPlexCreateFromDAG(rdm, 1, numPoints, coneSize, cones, coneOrientations, vertexCoords));
3917   } break;
3918   case DM_POLYTOPE_TETRAHEDRON: {
3919     PetscInt    numPoints[2]        = {4, 1};
3920     PetscInt    coneSize[5]         = {4, 0, 0, 0, 0};
3921     PetscInt    cones[4]            = {1, 2, 3, 4};
3922     PetscInt    coneOrientations[4] = {0, 0, 0, 0};
3923     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};
3924 
3925     PetscCall(DMSetDimension(rdm, 3));
3926     PetscCall(DMPlexCreateFromDAG(rdm, 1, numPoints, coneSize, cones, coneOrientations, vertexCoords));
3927   } break;
3928   case DM_POLYTOPE_HEXAHEDRON: {
3929     PetscInt    numPoints[2]        = {8, 1};
3930     PetscInt    coneSize[9]         = {8, 0, 0, 0, 0, 0, 0, 0, 0};
3931     PetscInt    cones[8]            = {1, 2, 3, 4, 5, 6, 7, 8};
3932     PetscInt    coneOrientations[8] = {0, 0, 0, 0, 0, 0, 0, 0};
3933     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};
3934 
3935     PetscCall(DMSetDimension(rdm, 3));
3936     PetscCall(DMPlexCreateFromDAG(rdm, 1, numPoints, coneSize, cones, coneOrientations, vertexCoords));
3937   } break;
3938   case DM_POLYTOPE_TRI_PRISM: {
3939     PetscInt    numPoints[2]        = {6, 1};
3940     PetscInt    coneSize[7]         = {6, 0, 0, 0, 0, 0, 0};
3941     PetscInt    cones[6]            = {1, 2, 3, 4, 5, 6};
3942     PetscInt    coneOrientations[6] = {0, 0, 0, 0, 0, 0};
3943     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};
3944 
3945     PetscCall(DMSetDimension(rdm, 3));
3946     PetscCall(DMPlexCreateFromDAG(rdm, 1, numPoints, coneSize, cones, coneOrientations, vertexCoords));
3947   } break;
3948   case DM_POLYTOPE_TRI_PRISM_TENSOR: {
3949     PetscInt    numPoints[2]        = {6, 1};
3950     PetscInt    coneSize[7]         = {6, 0, 0, 0, 0, 0, 0};
3951     PetscInt    cones[6]            = {1, 2, 3, 4, 5, 6};
3952     PetscInt    coneOrientations[6] = {0, 0, 0, 0, 0, 0};
3953     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};
3954 
3955     PetscCall(DMSetDimension(rdm, 3));
3956     PetscCall(DMPlexCreateFromDAG(rdm, 1, numPoints, coneSize, cones, coneOrientations, vertexCoords));
3957   } break;
3958   case DM_POLYTOPE_QUAD_PRISM_TENSOR: {
3959     PetscInt    numPoints[2]        = {8, 1};
3960     PetscInt    coneSize[9]         = {8, 0, 0, 0, 0, 0, 0, 0, 0};
3961     PetscInt    cones[8]            = {1, 2, 3, 4, 5, 6, 7, 8};
3962     PetscInt    coneOrientations[8] = {0, 0, 0, 0, 0, 0, 0, 0};
3963     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};
3964 
3965     PetscCall(DMSetDimension(rdm, 3));
3966     PetscCall(DMPlexCreateFromDAG(rdm, 1, numPoints, coneSize, cones, coneOrientations, vertexCoords));
3967   } break;
3968   case DM_POLYTOPE_PYRAMID: {
3969     PetscInt    numPoints[2]        = {5, 1};
3970     PetscInt    coneSize[6]         = {5, 0, 0, 0, 0, 0};
3971     PetscInt    cones[5]            = {1, 2, 3, 4, 5};
3972     PetscInt    coneOrientations[8] = {0, 0, 0, 0, 0, 0, 0, 0};
3973     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};
3974 
3975     PetscCall(DMSetDimension(rdm, 3));
3976     PetscCall(DMPlexCreateFromDAG(rdm, 1, numPoints, coneSize, cones, coneOrientations, vertexCoords));
3977   } break;
3978   default:
3979     SETERRQ(PetscObjectComm((PetscObject)rdm), PETSC_ERR_ARG_WRONG, "Cannot create reference cell for cell type %s", DMPolytopeTypes[ct]);
3980   }
3981   {
3982     PetscInt Nv, v;
3983 
3984     /* Must create the celltype label here so that we do not automatically try to compute the types */
3985     PetscCall(DMCreateLabel(rdm, "celltype"));
3986     PetscCall(DMPlexSetCellType(rdm, 0, ct));
3987     PetscCall(DMPlexGetChart(rdm, NULL, &Nv));
3988     for (v = 1; v < Nv; ++v) PetscCall(DMPlexSetCellType(rdm, v, DM_POLYTOPE_POINT));
3989   }
3990   PetscCall(DMPlexInterpolateInPlace_Internal(rdm));
3991   PetscCall(PetscObjectSetName((PetscObject)rdm, DMPolytopeTypes[ct]));
3992   PetscFunctionReturn(PETSC_SUCCESS);
3993 }
3994 
3995 /*@
3996   DMPlexCreateReferenceCell - Create a `DMPLEX` with the appropriate FEM reference cell
3997 
3998   Collective
3999 
4000   Input Parameters:
4001 + comm - The communicator
4002 - ct   - The cell type of the reference cell
4003 
4004   Output Parameter:
4005 . refdm - The reference cell
4006 
4007   Level: intermediate
4008 
4009 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateBoxMesh()`
4010 @*/
4011 PetscErrorCode DMPlexCreateReferenceCell(MPI_Comm comm, DMPolytopeType ct, DM *refdm)
4012 {
4013   PetscFunctionBegin;
4014   PetscCall(DMCreate(comm, refdm));
4015   PetscCall(DMSetType(*refdm, DMPLEX));
4016   PetscCall(DMPlexCreateReferenceCell_Internal(*refdm, ct));
4017   PetscFunctionReturn(PETSC_SUCCESS);
4018 }
4019 
4020 static PetscErrorCode DMPlexCreateBoundaryLabel_Private(DM dm, const char name[])
4021 {
4022   DM        plex;
4023   DMLabel   label;
4024   PetscBool hasLabel;
4025 
4026   PetscFunctionBegin;
4027   PetscCall(DMHasLabel(dm, name, &hasLabel));
4028   if (hasLabel) PetscFunctionReturn(PETSC_SUCCESS);
4029   PetscCall(DMCreateLabel(dm, name));
4030   PetscCall(DMGetLabel(dm, name, &label));
4031   PetscCall(DMConvert(dm, DMPLEX, &plex));
4032   PetscCall(DMPlexMarkBoundaryFaces(plex, 1, label));
4033   PetscCall(DMPlexLabelComplete(plex, label));
4034   PetscCall(DMDestroy(&plex));
4035   PetscFunctionReturn(PETSC_SUCCESS);
4036 }
4037 
4038 /*
4039   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.
4040 
4041     (x, y) -> (r, theta) = (x[1], (x[0] - lower[0]) * 2\pi/(upper[0] - lower[0]))
4042 */
4043 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[])
4044 {
4045   const PetscReal low = PetscRealPart(constants[0]);
4046   const PetscReal upp = PetscRealPart(constants[1]);
4047   const PetscReal r   = PetscRealPart(u[1]);
4048   const PetscReal th  = 2. * PETSC_PI * (PetscRealPart(u[0]) - low) / (upp - low);
4049 
4050   f0[0] = r * PetscCosReal(th);
4051   f0[1] = r * PetscSinReal(th);
4052 }
4053 
4054 // Insert vertices and their joins, marked by depth
4055 static PetscErrorCode ProcessCohesiveLabel_Vertices(DM dm, DMLabel label, DMLabel vlabel, PetscInt val, PetscInt n, const PetscInt vertices[])
4056 {
4057   PetscFunctionBegin;
4058   PetscCall(DMPlexMarkSubmesh_Interpolated(dm, vlabel, val, PETSC_FALSE, PETSC_FALSE, label, NULL));
4059   PetscFunctionReturn(PETSC_SUCCESS);
4060 }
4061 
4062 // Insert faces and their closures, marked by depth
4063 static PetscErrorCode ProcessCohesiveLabel_Faces(DM dm, DMLabel label, PetscInt n, const PetscInt faces[])
4064 {
4065   PetscFunctionBegin;
4066   for (PetscInt p = 0; p < n; ++p) {
4067     const PetscInt point   = faces[p];
4068     PetscInt      *closure = NULL;
4069     PetscInt       clSize, pdepth;
4070 
4071     PetscCall(DMPlexGetPointDepth(dm, point, &pdepth));
4072     PetscCall(DMLabelSetValue(label, point, pdepth));
4073     PetscCall(DMPlexGetTransitiveClosure(dm, point, PETSC_TRUE, &clSize, &closure));
4074     for (PetscInt cl = 0; cl < clSize * 2; cl += 2) {
4075       PetscCall(DMPlexGetPointDepth(dm, closure[cl], &pdepth));
4076       PetscCall(DMLabelSetValue(label, closure[cl], pdepth));
4077     }
4078     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, &clSize, &closure));
4079   }
4080   PetscFunctionReturn(PETSC_SUCCESS);
4081 }
4082 
4083 PETSC_EXTERN PetscErrorCode PetscOptionsFindPairPrefix_Private(PetscOptions, const char pre[], const char name[], const char *option[], const char *value[], PetscBool *flg);
4084 
4085 const char *const DMPlexShapes[] = {"box", "box_surface", "ball", "sphere", "cylinder", "schwarz_p", "gyroid", "doublet", "annulus", "hypercubic", "zbox", "unknown", "DMPlexShape", "DM_SHAPE_", NULL};
4086 
4087 static PetscErrorCode DMPlexCreateFromOptions_Internal(PetscOptionItems *PetscOptionsObject, PetscBool *useCoordSpace, DM dm)
4088 {
4089   DMPlexShape    shape   = DM_SHAPE_BOX;
4090   DMPolytopeType cell    = DM_POLYTOPE_TRIANGLE;
4091   PetscInt       dim     = 2;
4092   PetscBool      simplex = PETSC_TRUE, interpolate = PETSC_TRUE, adjCone = PETSC_FALSE, adjClosure = PETSC_TRUE, refDomain = PETSC_FALSE;
4093   PetscBool      flg, flg2, fflg, strflg, bdfflg, nameflg;
4094   MPI_Comm       comm;
4095   char           filename[PETSC_MAX_PATH_LEN]   = "<unspecified>";
4096   char           bdFilename[PETSC_MAX_PATH_LEN] = "<unspecified>";
4097   char           plexname[PETSC_MAX_PATH_LEN]   = "";
4098   const char    *option;
4099 
4100   PetscFunctionBegin;
4101   PetscCall(PetscLogEventBegin(DMPLEX_CreateFromOptions, dm, 0, 0, 0));
4102   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
4103   /* TODO Turn this into a registration interface */
4104   PetscCall(PetscOptionsString("-dm_plex_filename", "File containing a mesh", "DMPlexCreateFromFile", filename, filename, sizeof(filename), &fflg));
4105   PetscCall(PetscOptionsString("-dm_plex_file_contents", "Contents of a file format in a string", "DMPlexCreateFromFile", filename, filename, sizeof(filename), &strflg));
4106   PetscCall(PetscOptionsString("-dm_plex_boundary_filename", "File containing a mesh boundary", "DMPlexCreateFromFile", bdFilename, bdFilename, sizeof(bdFilename), &bdfflg));
4107   PetscCall(PetscOptionsString("-dm_plex_name", "Name of the mesh in the file", "DMPlexCreateFromFile", plexname, plexname, sizeof(plexname), &nameflg));
4108   PetscCall(PetscOptionsEnum("-dm_plex_cell", "Cell shape", "", DMPolytopeTypes, (PetscEnum)cell, (PetscEnum *)&cell, NULL));
4109   PetscCall(PetscOptionsBool("-dm_plex_reference_cell_domain", "Use a reference cell domain", "", refDomain, &refDomain, NULL));
4110   PetscCall(PetscOptionsEnum("-dm_plex_shape", "Shape for built-in mesh", "", DMPlexShapes, (PetscEnum)shape, (PetscEnum *)&shape, &flg));
4111   PetscCall(PetscOptionsBoundedInt("-dm_plex_dim", "Topological dimension of the mesh", "DMGetDimension", dim, &dim, &flg, 0));
4112   PetscCall(PetscOptionsBool("-dm_plex_simplex", "Mesh cell shape", "", simplex, &simplex, &flg));
4113   PetscCall(PetscOptionsBool("-dm_plex_interpolate", "Flag to create edges and faces automatically", "", interpolate, &interpolate, &flg));
4114   PetscCall(PetscOptionsBool("-dm_plex_adj_cone", "Set adjacency direction", "DMSetBasicAdjacency", adjCone, &adjCone, &flg));
4115   PetscCall(PetscOptionsBool("-dm_plex_adj_closure", "Set adjacency size", "DMSetBasicAdjacency", adjClosure, &adjClosure, &flg2));
4116   if (flg || flg2) PetscCall(DMSetBasicAdjacency(dm, adjCone, adjClosure));
4117 
4118   switch (cell) {
4119   case DM_POLYTOPE_POINT:
4120   case DM_POLYTOPE_SEGMENT:
4121   case DM_POLYTOPE_POINT_PRISM_TENSOR:
4122   case DM_POLYTOPE_TRIANGLE:
4123   case DM_POLYTOPE_QUADRILATERAL:
4124   case DM_POLYTOPE_TETRAHEDRON:
4125   case DM_POLYTOPE_HEXAHEDRON:
4126     *useCoordSpace = PETSC_TRUE;
4127     break;
4128   default:
4129     *useCoordSpace = PETSC_FALSE;
4130     break;
4131   }
4132 
4133   if (fflg) {
4134     DM          dmnew;
4135     const char *name;
4136 
4137     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
4138     PetscCall(DMPlexCreateFromFile(PetscObjectComm((PetscObject)dm), filename, nameflg ? plexname : name, interpolate, &dmnew));
4139     PetscCall(DMPlexCopy_Internal(dm, PETSC_FALSE, PETSC_FALSE, dmnew));
4140     PetscCall(DMPlexReplace_Internal(dm, &dmnew));
4141   } else if (refDomain) {
4142     PetscCall(DMPlexCreateReferenceCell_Internal(dm, cell));
4143   } else if (bdfflg) {
4144     DM          bdm, dmnew;
4145     const char *name;
4146 
4147     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
4148     PetscCall(DMPlexCreateFromFile(PetscObjectComm((PetscObject)dm), bdFilename, nameflg ? plexname : name, interpolate, &bdm));
4149     PetscCall(PetscObjectSetOptionsPrefix((PetscObject)bdm, "bd_"));
4150     PetscCall(DMSetFromOptions(bdm));
4151     PetscCall(DMPlexGenerate(bdm, NULL, interpolate, &dmnew));
4152     PetscCall(DMDestroy(&bdm));
4153     PetscCall(DMPlexCopy_Internal(dm, PETSC_FALSE, PETSC_FALSE, dmnew));
4154     PetscCall(DMPlexReplace_Internal(dm, &dmnew));
4155   } else if (strflg) {
4156     DM          dmnew;
4157     PetscViewer viewer;
4158     const char *contents;
4159     char       *strname;
4160     char        tmpdir[PETSC_MAX_PATH_LEN];
4161     char        tmpfilename[PETSC_MAX_PATH_LEN];
4162     char        name[PETSC_MAX_PATH_LEN];
4163     MPI_Comm    comm;
4164     PetscMPIInt rank;
4165 
4166     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
4167     PetscCallMPI(MPI_Comm_rank(comm, &rank));
4168     PetscCall(PetscStrchr(filename, ':', &strname));
4169     PetscCheck(strname, comm, PETSC_ERR_ARG_WRONG, "File contents must have the form \"ext:string_name\", not %s", filename);
4170     strname[0] = '\0';
4171     ++strname;
4172     PetscCall(PetscDLSym(NULL, strname, (void **)&contents));
4173     PetscCheck(contents, comm, PETSC_ERR_ARG_WRONG, "Could not locate mesh string %s", strname);
4174     PetscCall(PetscGetTmp(comm, tmpdir, PETSC_MAX_PATH_LEN));
4175     PetscCall(PetscStrlcat(tmpdir, "/meshXXXXXX", PETSC_MAX_PATH_LEN));
4176     PetscCall(PetscMkdtemp(tmpdir));
4177     PetscCall(PetscSNPrintf(tmpfilename, PETSC_MAX_PATH_LEN, "%s/mesh.%s", tmpdir, filename));
4178     PetscCall(PetscViewerASCIIOpen(comm, tmpfilename, &viewer));
4179     PetscCall(PetscViewerASCIIPrintf(viewer, "%s\n", contents));
4180     PetscCall(PetscViewerDestroy(&viewer));
4181     PetscCall(DMPlexCreateFromFile(PetscObjectComm((PetscObject)dm), tmpfilename, plexname, interpolate, &dmnew));
4182     PetscCall(PetscRMTree(tmpdir));
4183     PetscCall(PetscSNPrintf(name, PETSC_MAX_PATH_LEN, "%s Mesh", strname));
4184     PetscCall(PetscObjectSetName((PetscObject)dm, name));
4185     PetscCall(DMPlexCopy_Internal(dm, PETSC_FALSE, PETSC_FALSE, dmnew));
4186     PetscCall(DMPlexReplace_Internal(dm, &dmnew));
4187   } else {
4188     PetscCall(PetscObjectSetName((PetscObject)dm, DMPlexShapes[shape]));
4189     switch (shape) {
4190     case DM_SHAPE_BOX:
4191     case DM_SHAPE_ZBOX:
4192     case DM_SHAPE_ANNULUS: {
4193       PetscInt       faces[3]  = {0, 0, 0};
4194       PetscReal      lower[3]  = {0, 0, 0};
4195       PetscReal      upper[3]  = {1, 1, 1};
4196       DMBoundaryType bdt[3]    = {DM_BOUNDARY_NONE, DM_BOUNDARY_NONE, DM_BOUNDARY_NONE};
4197       PetscBool      isAnnular = shape == DM_SHAPE_ANNULUS ? PETSC_TRUE : PETSC_FALSE;
4198       PetscInt       i, n;
4199 
4200       n = dim;
4201       for (i = 0; i < dim; ++i) faces[i] = (dim == 1 ? 1 : 4 - dim);
4202       PetscCall(PetscOptionsIntArray("-dm_plex_box_faces", "Number of faces along each dimension", "", faces, &n, &flg));
4203       n = 3;
4204       PetscCall(PetscOptionsRealArray("-dm_plex_box_lower", "Lower left corner of box", "", lower, &n, &flg));
4205       PetscCheck(!flg || !(n != dim), comm, PETSC_ERR_ARG_SIZ, "Lower box point had %" PetscInt_FMT " values, should have been %" PetscInt_FMT, n, dim);
4206       n = 3;
4207       PetscCall(PetscOptionsRealArray("-dm_plex_box_upper", "Upper right corner of box", "", upper, &n, &flg));
4208       PetscCheck(!flg || !(n != dim), comm, PETSC_ERR_ARG_SIZ, "Upper box point had %" PetscInt_FMT " values, should have been %" PetscInt_FMT, n, dim);
4209       n = 3;
4210       PetscCall(PetscOptionsEnumArray("-dm_plex_box_bd", "Boundary type for each dimension", "", DMBoundaryTypes, (PetscEnum *)bdt, &n, &flg));
4211       PetscCheck(!flg || !(n != dim), comm, PETSC_ERR_ARG_SIZ, "Box boundary types had %" PetscInt_FMT " values, should have been %" PetscInt_FMT, n, dim);
4212 
4213       PetscCheck(!isAnnular || dim == 2, comm, PETSC_ERR_ARG_OUTOFRANGE, "Only two dimensional annuli have been implemented");
4214       if (isAnnular)
4215         for (i = 0; i < dim - 1; ++i) bdt[i] = DM_BOUNDARY_PERIODIC;
4216 
4217       switch (cell) {
4218       case DM_POLYTOPE_TRI_PRISM_TENSOR:
4219         PetscCall(DMPlexCreateWedgeBoxMesh_Internal(dm, faces, lower, upper, bdt));
4220         if (!interpolate) {
4221           DM udm;
4222 
4223           PetscCall(DMPlexUninterpolate(dm, &udm));
4224           PetscCall(DMPlexReplace_Internal(dm, &udm));
4225         }
4226         break;
4227       default:
4228         PetscCall(DMPlexCreateBoxMesh_Internal(dm, shape, dim, simplex, faces, lower, upper, bdt, interpolate));
4229         break;
4230       }
4231       if (isAnnular) {
4232         DM          cdm;
4233         PetscDS     cds;
4234         PetscScalar bounds[2] = {lower[0], upper[0]};
4235 
4236         // Fix coordinates for annular region
4237         PetscCall(DMSetPeriodicity(dm, NULL, NULL, NULL));
4238         PetscCall(DMSetCellCoordinatesLocal(dm, NULL));
4239         PetscCall(DMSetCellCoordinates(dm, NULL));
4240         PetscCall(DMPlexCreateCoordinateSpace(dm, 1, PETSC_TRUE, NULL));
4241         PetscCall(DMGetCoordinateDM(dm, &cdm));
4242         PetscCall(DMGetDS(cdm, &cds));
4243         PetscCall(PetscDSSetConstants(cds, 2, bounds));
4244         PetscCall(DMPlexRemapGeometry(dm, 0.0, boxToAnnulus));
4245       }
4246     } break;
4247     case DM_SHAPE_BOX_SURFACE: {
4248       PetscInt  faces[3] = {0, 0, 0};
4249       PetscReal lower[3] = {0, 0, 0};
4250       PetscReal upper[3] = {1, 1, 1};
4251       PetscInt  i, n;
4252 
4253       n = dim + 1;
4254       for (i = 0; i < dim + 1; ++i) faces[i] = (dim + 1 == 1 ? 1 : 4 - (dim + 1));
4255       PetscCall(PetscOptionsIntArray("-dm_plex_box_faces", "Number of faces along each dimension", "", faces, &n, &flg));
4256       n = 3;
4257       PetscCall(PetscOptionsRealArray("-dm_plex_box_lower", "Lower left corner of box", "", lower, &n, &flg));
4258       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);
4259       n = 3;
4260       PetscCall(PetscOptionsRealArray("-dm_plex_box_upper", "Upper right corner of box", "", upper, &n, &flg));
4261       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);
4262       PetscCall(DMPlexCreateBoxSurfaceMesh_Internal(dm, dim + 1, faces, lower, upper, interpolate));
4263     } break;
4264     case DM_SHAPE_SPHERE: {
4265       PetscReal R = 1.0;
4266 
4267       PetscCall(PetscOptionsReal("-dm_plex_sphere_radius", "Radius of the sphere", "", R, &R, &flg));
4268       PetscCall(DMPlexCreateSphereMesh_Internal(dm, dim, simplex, R));
4269     } break;
4270     case DM_SHAPE_BALL: {
4271       PetscReal R = 1.0;
4272 
4273       PetscCall(PetscOptionsReal("-dm_plex_ball_radius", "Radius of the ball", "", R, &R, &flg));
4274       PetscCall(DMPlexCreateBallMesh_Internal(dm, dim, R));
4275     } break;
4276     case DM_SHAPE_CYLINDER: {
4277       DMBoundaryType bdt = DM_BOUNDARY_NONE;
4278       PetscInt       Nw  = 6;
4279       PetscInt       Nr  = 0;
4280 
4281       PetscCall(PetscOptionsEnum("-dm_plex_cylinder_bd", "Boundary type in the z direction", "", DMBoundaryTypes, (PetscEnum)bdt, (PetscEnum *)&bdt, NULL));
4282       PetscCall(PetscOptionsInt("-dm_plex_cylinder_num_wedges", "Number of wedges around the cylinder", "", Nw, &Nw, NULL));
4283       PetscCall(PetscOptionsInt("-dm_plex_cylinder_num_refine", "Number of refinements before projection", "", Nr, &Nr, NULL));
4284       switch (cell) {
4285       case DM_POLYTOPE_TRI_PRISM_TENSOR:
4286         PetscCall(DMPlexCreateWedgeCylinderMesh_Internal(dm, Nw, interpolate));
4287         break;
4288       default:
4289         PetscCall(DMPlexCreateHexCylinderMesh_Internal(dm, bdt, Nr));
4290         break;
4291       }
4292     } break;
4293     case DM_SHAPE_SCHWARZ_P: // fallthrough
4294     case DM_SHAPE_GYROID: {
4295       PetscInt       extent[3] = {1, 1, 1}, refine = 0, layers = 0, three;
4296       PetscReal      thickness   = 0.;
4297       DMBoundaryType periodic[3] = {DM_BOUNDARY_NONE, DM_BOUNDARY_NONE, DM_BOUNDARY_NONE};
4298       DMPlexTPSType  tps_type    = shape == DM_SHAPE_SCHWARZ_P ? DMPLEX_TPS_SCHWARZ_P : DMPLEX_TPS_GYROID;
4299       PetscBool      tps_distribute;
4300       PetscCall(PetscOptionsIntArray("-dm_plex_tps_extent", "Number of replicas for each of three dimensions", NULL, extent, (three = 3, &three), NULL));
4301       PetscCall(PetscOptionsInt("-dm_plex_tps_refine", "Number of refinements", NULL, refine, &refine, NULL));
4302       PetscCall(PetscOptionsEnumArray("-dm_plex_tps_periodic", "Periodicity in each of three dimensions", NULL, DMBoundaryTypes, (PetscEnum *)periodic, (three = 3, &three), NULL));
4303       PetscCall(PetscOptionsInt("-dm_plex_tps_layers", "Number of layers in volumetric extrusion (or zero to not extrude)", NULL, layers, &layers, NULL));
4304       PetscCall(PetscOptionsReal("-dm_plex_tps_thickness", "Thickness of volumetric extrusion", NULL, thickness, &thickness, NULL));
4305       PetscCall(DMPlexDistributeGetDefault(dm, &tps_distribute));
4306       PetscCall(PetscOptionsBool("-dm_plex_tps_distribute", "Distribute the 2D mesh prior to refinement and extrusion", NULL, tps_distribute, &tps_distribute, NULL));
4307       PetscCall(DMPlexCreateTPSMesh_Internal(dm, tps_type, extent, periodic, tps_distribute, refine, layers, thickness));
4308     } break;
4309     case DM_SHAPE_DOUBLET: {
4310       DM        dmnew;
4311       PetscReal rl = 0.0;
4312 
4313       PetscCall(PetscOptionsReal("-dm_plex_doublet_refinementlimit", "Refinement limit", NULL, rl, &rl, NULL));
4314       PetscCall(DMPlexCreateDoublet(PetscObjectComm((PetscObject)dm), dim, simplex, interpolate, rl, &dmnew));
4315       PetscCall(DMPlexCopy_Internal(dm, PETSC_FALSE, PETSC_FALSE, dmnew));
4316       PetscCall(DMPlexReplace_Internal(dm, &dmnew));
4317     } break;
4318     case DM_SHAPE_HYPERCUBIC: {
4319       PetscInt       *edges;
4320       PetscReal      *lower, *upper;
4321       DMBoundaryType *bdt;
4322       PetscInt        n, d;
4323 
4324       *useCoordSpace = PETSC_FALSE;
4325       PetscCall(PetscMalloc4(dim, &edges, dim, &lower, dim, &upper, dim, &bdt));
4326       for (d = 0; d < dim; ++d) {
4327         edges[d] = 1;
4328         lower[d] = 0.;
4329         upper[d] = 1.;
4330         bdt[d]   = DM_BOUNDARY_PERIODIC;
4331       }
4332       n = dim;
4333       PetscCall(PetscOptionsIntArray("-dm_plex_box_faces", "Number of faces along each dimension", "", edges, &n, &flg));
4334       n = dim;
4335       PetscCall(PetscOptionsRealArray("-dm_plex_box_lower", "Lower left corner of box", "", lower, &n, &flg));
4336       PetscCheck(!flg || n == dim, comm, PETSC_ERR_ARG_SIZ, "Lower box point had %" PetscInt_FMT " values, should have been %" PetscInt_FMT, n, dim);
4337       n = dim;
4338       PetscCall(PetscOptionsRealArray("-dm_plex_box_upper", "Upper right corner of box", "", upper, &n, &flg));
4339       PetscCheck(!flg || n == dim, comm, PETSC_ERR_ARG_SIZ, "Upper box point had %" PetscInt_FMT " values, should have been %" PetscInt_FMT, n, dim);
4340       n = dim;
4341       PetscCall(PetscOptionsEnumArray("-dm_plex_box_bd", "Boundary type for each dimension", "", DMBoundaryTypes, (PetscEnum *)bdt, &n, &flg));
4342       PetscCheck(!flg || n == dim, comm, PETSC_ERR_ARG_SIZ, "Box boundary types had %" PetscInt_FMT " values, should have been %" PetscInt_FMT, n, dim);
4343       PetscCall(DMPlexCreateHypercubicMesh_Internal(dm, dim, lower, upper, edges, bdt));
4344       PetscCall(PetscFree4(edges, lower, upper, bdt));
4345     } break;
4346     default:
4347       SETERRQ(comm, PETSC_ERR_SUP, "Domain shape %s is unsupported", DMPlexShapes[shape]);
4348     }
4349   }
4350   PetscCall(DMPlexSetRefinementUniform(dm, PETSC_TRUE));
4351   if (!((PetscObject)dm)->name && nameflg) PetscCall(PetscObjectSetName((PetscObject)dm, plexname));
4352   // Allow label creation
4353   PetscCall(PetscOptionsFindPairPrefix_Private(NULL, ((PetscObject)dm)->prefix, "-dm_plex_label_", &option, NULL, &flg));
4354   if (flg) {
4355     DMLabel     label;
4356     PetscInt    points[1024], n = 1024;
4357     char        fulloption[PETSC_MAX_PATH_LEN];
4358     const char *name = &option[14];
4359 
4360     PetscCall(DMCreateLabel(dm, name));
4361     PetscCall(DMGetLabel(dm, name, &label));
4362     fulloption[0] = '-';
4363     fulloption[1] = 0;
4364     PetscCall(PetscStrlcat(fulloption, option, PETSC_MAX_PATH_LEN));
4365     PetscCall(PetscOptionsGetIntArray(NULL, ((PetscObject)dm)->prefix, fulloption, points, &n, NULL));
4366     for (PetscInt p = 0; p < n; ++p) PetscCall(DMLabelSetValue(label, points[p], 1));
4367   }
4368   // Allow cohesive label creation
4369   //   Faces are input, completed, and all points are marked with their depth
4370   PetscCall(PetscOptionsFindPairPrefix_Private(NULL, ((PetscObject)dm)->prefix, "-dm_plex_cohesive_label_", &option, NULL, &flg));
4371   if (flg) {
4372     DMLabel   label;
4373     PetscInt  points[1024], n, pStart, pEnd, Nl = 1;
4374     PetscBool noCreate = PETSC_FALSE;
4375     char      fulloption[PETSC_MAX_PATH_LEN];
4376     char      name[PETSC_MAX_PATH_LEN];
4377     size_t    len;
4378 
4379     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4380     PetscCall(PetscStrncpy(name, &option[23], PETSC_MAX_PATH_LEN));
4381     PetscCall(PetscStrlen(name, &len));
4382     if (name[len - 1] == '0') Nl = 10;
4383     for (PetscInt l = 0; l < Nl; ++l) {
4384       if (l > 0) name[len - 1] = (char)('0' + l);
4385       fulloption[0] = 0;
4386       PetscCall(PetscStrlcat(fulloption, "-dm_plex_cohesive_label_", 32));
4387       PetscCall(PetscStrlcat(fulloption, name, PETSC_MAX_PATH_LEN - 32));
4388       n = 1024;
4389       PetscCall(PetscOptionsGetIntArray(NULL, ((PetscObject)dm)->prefix, fulloption, points, &n, &flg));
4390       if (!flg) break;
4391       PetscCall(DMHasLabel(dm, name, &noCreate));
4392       if (noCreate) {
4393         DMLabel         inlabel;
4394         IS              pointIS;
4395         const PetscInt *lpoints;
4396         PetscInt        pdep, ln, inval = points[0];
4397         char            newname[PETSC_MAX_PATH_LEN];
4398 
4399         PetscCheck(n == 1, comm, PETSC_ERR_ARG_WRONG, "Must specify a label value with this option");
4400         PetscCall(DMGetLabel(dm, name, &inlabel));
4401         PetscCall(DMLabelGetStratumIS(inlabel, inval, &pointIS));
4402         PetscCall(ISGetLocalSize(pointIS, &ln));
4403         PetscCall(ISGetIndices(pointIS, &lpoints));
4404         PetscCall(DMPlexGetPointDepth(dm, lpoints[0], &pdep));
4405         PetscCall(PetscSNPrintf(newname, PETSC_MAX_PATH_LEN, "%s%" PetscInt_FMT, name, points[0]));
4406         PetscCall(DMCreateLabel(dm, newname));
4407         PetscCall(DMGetLabel(dm, newname, &label));
4408         if (!pdep) PetscCall(ProcessCohesiveLabel_Vertices(dm, label, inlabel, inval, ln, lpoints));
4409         else PetscCall(ProcessCohesiveLabel_Faces(dm, label, ln, lpoints));
4410         PetscCall(ISRestoreIndices(pointIS, &lpoints));
4411         PetscCall(ISDestroy(&pointIS));
4412       } else {
4413         PetscCall(DMCreateLabel(dm, name));
4414         PetscCall(DMGetLabel(dm, name, &label));
4415         if (pStart >= pEnd) n = 0;
4416         PetscCall(ProcessCohesiveLabel_Faces(dm, label, n, points));
4417       }
4418       PetscCall(DMPlexOrientLabel(dm, label));
4419       PetscCall(DMPlexLabelCohesiveComplete(dm, label, NULL, 1, PETSC_FALSE, PETSC_FALSE, NULL));
4420     }
4421   }
4422   PetscCall(DMViewFromOptions(dm, NULL, "-created_dm_view"));
4423   PetscCall(PetscLogEventEnd(DMPLEX_CreateFromOptions, dm, 0, 0, 0));
4424   PetscFunctionReturn(PETSC_SUCCESS);
4425 }
4426 
4427 PetscErrorCode DMSetFromOptions_NonRefinement_Plex(DM dm, PetscOptionItems *PetscOptionsObject)
4428 {
4429   DM_Plex  *mesh = (DM_Plex *)dm->data;
4430   PetscBool flg, flg2;
4431   char      bdLabel[PETSC_MAX_PATH_LEN];
4432   char      method[PETSC_MAX_PATH_LEN];
4433 
4434   PetscFunctionBegin;
4435   /* Handle viewing */
4436   PetscCall(PetscOptionsBool("-dm_plex_print_set_values", "Output all set values info", "DMPlexMatSetClosure", PETSC_FALSE, &mesh->printSetValues, NULL));
4437   PetscCall(PetscOptionsBoundedInt("-dm_plex_print_fem", "Debug output level for all fem computations", "DMPlexSNESComputeResidualFEM", 0, &mesh->printFEM, NULL, 0));
4438   PetscCall(PetscOptionsBoundedInt("-dm_plex_print_fvm", "Debug output level for all fvm computations", "DMPlexSNESComputeResidualFVM", 0, &mesh->printFVM, NULL, 0));
4439   PetscCall(PetscOptionsReal("-dm_plex_print_tol", "Tolerance for FEM output", "DMPlexSNESComputeResidualFEM", mesh->printTol, &mesh->printTol, NULL));
4440   PetscCall(PetscOptionsBoundedInt("-dm_plex_print_l2", "Debug output level all L2 diff computations", "DMComputeL2Diff", 0, &mesh->printL2, NULL, 0));
4441   PetscCall(PetscOptionsBoundedInt("-dm_plex_print_locate", "Debug output level all point location computations", "DMLocatePoints", 0, &mesh->printLocate, NULL, 0));
4442   PetscCall(PetscOptionsBoundedInt("-dm_plex_print_project", "Debug output level all projection computations", "DMPlexProject", 0, &mesh->printProject, NULL, 0));
4443   PetscCall(DMMonitorSetFromOptions(dm, "-dm_plex_monitor_throughput", "Monitor the simulation throughput", "DMPlexMonitorThroughput", DMPlexMonitorThroughput, NULL, &flg));
4444   if (flg) PetscCall(PetscLogDefaultBegin());
4445   /* Labeling */
4446   PetscCall(PetscOptionsString("-dm_plex_boundary_label", "Label to mark the mesh boundary", "", bdLabel, bdLabel, sizeof(bdLabel), &flg));
4447   if (flg) PetscCall(DMPlexCreateBoundaryLabel_Private(dm, bdLabel));
4448   /* Point Location */
4449   PetscCall(PetscOptionsBool("-dm_plex_hash_location", "Use grid hashing for point location", "DMInterpolate", PETSC_FALSE, &mesh->useHashLocation, NULL));
4450   /* Partitioning and distribution */
4451   PetscCall(PetscOptionsBool("-dm_plex_partition_balance", "Attempt to evenly divide points on partition boundary between processes", "DMPlexSetPartitionBalance", PETSC_FALSE, &mesh->partitionBalance, NULL));
4452   /* Reordering */
4453   PetscCall(PetscOptionsBool("-dm_reorder_section", "Compute point permutation for local section", "DMReorderSectionSetDefault", PETSC_FALSE, &flg2, &flg));
4454   if (flg) PetscCall(DMReorderSectionSetDefault(dm, flg2 ? DM_REORDER_DEFAULT_TRUE : DM_REORDER_DEFAULT_FALSE));
4455   PetscCall(PetscOptionsString("-dm_reorder_section_type", "Reordering method for local section", "DMReorderSectionSetType", method, method, PETSC_MAX_PATH_LEN, &flg));
4456   if (flg) PetscCall(DMReorderSectionSetType(dm, method));
4457   /* Generation and remeshing */
4458   PetscCall(PetscOptionsBool("-dm_plex_remesh_bd", "Allow changes to the boundary on remeshing", "DMAdapt", PETSC_FALSE, &mesh->remeshBd, NULL));
4459   /* Projection behavior */
4460   PetscCall(PetscOptionsBoundedInt("-dm_plex_max_projection_height", "Maximum mesh point height used to project locally", "DMPlexSetMaxProjectionHeight", 0, &mesh->maxProjectionHeight, NULL, 0));
4461   PetscCall(PetscOptionsBool("-dm_plex_regular_refinement", "Use special nested projection algorithm for regular refinement", "DMPlexSetRegularRefinement", mesh->regularRefinement, &mesh->regularRefinement, NULL));
4462   /* Checking structure */
4463   {
4464     PetscBool all = PETSC_FALSE;
4465 
4466     PetscCall(PetscOptionsBool("-dm_plex_check_all", "Perform all basic checks", "DMPlexCheck", PETSC_FALSE, &all, NULL));
4467     if (all) {
4468       PetscCall(DMPlexCheck(dm));
4469     } else {
4470       PetscCall(PetscOptionsBool("-dm_plex_check_symmetry", "Check that the adjacency information in the mesh is symmetric", "DMPlexCheckSymmetry", PETSC_FALSE, &flg, &flg2));
4471       if (flg && flg2) PetscCall(DMPlexCheckSymmetry(dm));
4472       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));
4473       if (flg && flg2) PetscCall(DMPlexCheckSkeleton(dm, 0));
4474       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));
4475       if (flg && flg2) PetscCall(DMPlexCheckFaces(dm, 0));
4476       PetscCall(PetscOptionsBool("-dm_plex_check_geometry", "Check that cells have positive volume", "DMPlexCheckGeometry", PETSC_FALSE, &flg, &flg2));
4477       if (flg && flg2) PetscCall(DMPlexCheckGeometry(dm));
4478       PetscCall(PetscOptionsBool("-dm_plex_check_pointsf", "Check some necessary conditions for PointSF", "DMPlexCheckPointSF", PETSC_FALSE, &flg, &flg2));
4479       if (flg && flg2) PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
4480       PetscCall(PetscOptionsBool("-dm_plex_check_interface_cones", "Check points on inter-partition interfaces have conforming order of cone points", "DMPlexCheckInterfaceCones", PETSC_FALSE, &flg, &flg2));
4481       if (flg && flg2) PetscCall(DMPlexCheckInterfaceCones(dm));
4482     }
4483     PetscCall(PetscOptionsBool("-dm_plex_check_cell_shape", "Check cell shape", "DMPlexCheckCellShape", PETSC_FALSE, &flg, &flg2));
4484     if (flg && flg2) PetscCall(DMPlexCheckCellShape(dm, PETSC_TRUE, PETSC_DETERMINE));
4485   }
4486   {
4487     PetscReal scale = 1.0;
4488 
4489     PetscCall(PetscOptionsReal("-dm_plex_scale", "Scale factor for mesh coordinates", "DMPlexScale", scale, &scale, &flg));
4490     if (flg) {
4491       Vec coordinates, coordinatesLocal;
4492 
4493       PetscCall(DMGetCoordinates(dm, &coordinates));
4494       PetscCall(DMGetCoordinatesLocal(dm, &coordinatesLocal));
4495       PetscCall(VecScale(coordinates, scale));
4496       PetscCall(VecScale(coordinatesLocal, scale));
4497     }
4498   }
4499   PetscCall(PetscPartitionerSetFromOptions(mesh->partitioner));
4500   PetscFunctionReturn(PETSC_SUCCESS);
4501 }
4502 
4503 PetscErrorCode DMSetFromOptions_Overlap_Plex(DM dm, PetscOptionItems *PetscOptionsObject, PetscInt *overlap)
4504 {
4505   PetscInt  numOvLabels = 16, numOvExLabels = 16;
4506   char     *ovLabelNames[16], *ovExLabelNames[16];
4507   PetscInt  numOvValues = 16, numOvExValues = 16, l;
4508   PetscBool flg;
4509 
4510   PetscFunctionBegin;
4511   PetscCall(PetscOptionsBoundedInt("-dm_distribute_overlap", "The size of the overlap halo", "DMPlexDistribute", *overlap, overlap, NULL, 0));
4512   PetscCall(PetscOptionsStringArray("-dm_distribute_overlap_labels", "List of overlap label names", "DMPlexDistribute", ovLabelNames, &numOvLabels, &flg));
4513   if (!flg) numOvLabels = 0;
4514   if (numOvLabels) {
4515     ((DM_Plex *)dm->data)->numOvLabels = numOvLabels;
4516     for (l = 0; l < numOvLabels; ++l) {
4517       PetscCall(DMGetLabel(dm, ovLabelNames[l], &((DM_Plex *)dm->data)->ovLabels[l]));
4518       PetscCheck(((DM_Plex *)dm->data)->ovLabels[l], PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Invalid label name %s", ovLabelNames[l]);
4519       PetscCall(PetscFree(ovLabelNames[l]));
4520     }
4521     PetscCall(PetscOptionsIntArray("-dm_distribute_overlap_values", "List of overlap label values", "DMPlexDistribute", ((DM_Plex *)dm->data)->ovValues, &numOvValues, &flg));
4522     if (!flg) numOvValues = 0;
4523     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);
4524 
4525     PetscCall(PetscOptionsStringArray("-dm_distribute_overlap_exclude_labels", "List of overlap exclude label names", "DMPlexDistribute", ovExLabelNames, &numOvExLabels, &flg));
4526     if (!flg) numOvExLabels = 0;
4527     ((DM_Plex *)dm->data)->numOvExLabels = numOvExLabels;
4528     for (l = 0; l < numOvExLabels; ++l) {
4529       PetscCall(DMGetLabel(dm, ovExLabelNames[l], &((DM_Plex *)dm->data)->ovExLabels[l]));
4530       PetscCheck(((DM_Plex *)dm->data)->ovExLabels[l], PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Invalid label name %s", ovExLabelNames[l]);
4531       PetscCall(PetscFree(ovExLabelNames[l]));
4532     }
4533     PetscCall(PetscOptionsIntArray("-dm_distribute_overlap_exclude_values", "List of overlap exclude label values", "DMPlexDistribute", ((DM_Plex *)dm->data)->ovExValues, &numOvExValues, &flg));
4534     if (!flg) numOvExValues = 0;
4535     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);
4536   }
4537   PetscFunctionReturn(PETSC_SUCCESS);
4538 }
4539 
4540 static PetscErrorCode DMSetFromOptions_Plex(DM dm, PetscOptionItems *PetscOptionsObject)
4541 {
4542   PetscFunctionList    ordlist;
4543   char                 oname[256];
4544   char                 sublabelname[PETSC_MAX_PATH_LEN] = "";
4545   DMReorderDefaultFlag reorder;
4546   PetscReal            volume    = -1.0;
4547   PetscInt             prerefine = 0, refine = 0, r, coarsen = 0, overlap = 0, extLayers = 0, dim;
4548   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;
4549 
4550   PetscFunctionBegin;
4551   PetscOptionsHeadBegin(PetscOptionsObject, "DMPlex Options");
4552   if (dm->cloneOpts) goto non_refine;
4553   /* Handle automatic creation */
4554   PetscCall(DMGetDimension(dm, &dim));
4555   if (dim < 0) {
4556     PetscCall(DMPlexCreateFromOptions_Internal(PetscOptionsObject, &coordSpace, dm));
4557     created = PETSC_TRUE;
4558   }
4559   PetscCall(DMGetDimension(dm, &dim));
4560   /* Handle interpolation before distribution */
4561   PetscCall(PetscOptionsBool("-dm_plex_interpolate_pre", "Flag to interpolate mesh before distribution", "", interpolate, &interpolate, &flg));
4562   if (flg) {
4563     DMPlexInterpolatedFlag interpolated;
4564 
4565     PetscCall(DMPlexIsInterpolated(dm, &interpolated));
4566     if (interpolated == DMPLEX_INTERPOLATED_FULL && !interpolate) {
4567       DM udm;
4568 
4569       PetscCall(DMPlexUninterpolate(dm, &udm));
4570       PetscCall(DMPlexReplace_Internal(dm, &udm));
4571     } else if (interpolated != DMPLEX_INTERPOLATED_FULL && interpolate) {
4572       DM idm;
4573 
4574       PetscCall(DMPlexInterpolate(dm, &idm));
4575       PetscCall(DMPlexReplace_Internal(dm, &idm));
4576     }
4577   }
4578   // Handle submesh selection before distribution
4579   PetscCall(PetscOptionsString("-dm_plex_submesh", "Label to use for submesh selection", "", sublabelname, sublabelname, PETSC_MAX_PATH_LEN, &flg));
4580   if (flg) {
4581     DM              subdm;
4582     DMLabel         label;
4583     IS              valueIS, pointIS;
4584     const PetscInt *values, *points;
4585     PetscBool       markedFaces = PETSC_FALSE;
4586     PetscInt        Nv, value, Np;
4587 
4588     PetscCall(DMGetLabel(dm, sublabelname, &label));
4589     PetscCall(DMLabelGetNumValues(label, &Nv));
4590     PetscCheck(Nv == 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Only a single label value is currently supported for submesh selection, not %" PetscInt_FMT, Nv);
4591     PetscCall(DMLabelGetValueIS(label, &valueIS));
4592     PetscCall(ISGetIndices(valueIS, &values));
4593     value = values[0];
4594     PetscCall(ISRestoreIndices(valueIS, &values));
4595     PetscCall(ISDestroy(&valueIS));
4596     PetscCall(DMLabelGetStratumSize(label, value, &Np));
4597     PetscCall(DMLabelGetStratumIS(label, value, &pointIS));
4598     PetscCall(ISGetIndices(pointIS, &points));
4599     for (PetscInt p = 0; p < Np; ++p) {
4600       PetscInt pdepth;
4601 
4602       PetscCall(DMPlexGetPointDepth(dm, points[p], &pdepth));
4603       if (pdepth) {
4604         markedFaces = PETSC_TRUE;
4605         break;
4606       }
4607     }
4608     PetscCall(ISRestoreIndices(pointIS, &points));
4609     PetscCall(ISDestroy(&pointIS));
4610     PetscCall(DMPlexCreateSubmesh(dm, label, value, markedFaces, &subdm));
4611     PetscCall(DMPlexReplace_Internal(dm, &subdm));
4612     PetscCall(DMSetFromOptions_NonRefinement_Plex(dm, PetscOptionsObject));
4613   }
4614   /* Handle DMPlex refinement before distribution */
4615   PetscCall(DMPlexGetRefinementUniform(dm, &uniformOrig));
4616   PetscCall(PetscOptionsBoundedInt("-dm_refine_pre", "The number of refinements before distribution", "DMCreate", prerefine, &prerefine, NULL, 0));
4617   PetscCall(PetscOptionsBool("-dm_refine_remap_pre", "Flag to control coordinate remapping", "DMCreate", remap, &remap, NULL));
4618   PetscCall(PetscOptionsBool("-dm_refine_uniform_pre", "Flag for uniform refinement before distribution", "DMCreate", uniform, &uniform, &flg));
4619   if (flg) PetscCall(DMPlexSetRefinementUniform(dm, uniform));
4620   PetscCall(PetscOptionsReal("-dm_refine_volume_limit_pre", "The maximum cell volume after refinement before distribution", "DMCreate", volume, &volume, &flg));
4621   if (flg) {
4622     PetscCall(DMPlexSetRefinementUniform(dm, PETSC_FALSE));
4623     PetscCall(DMPlexSetRefinementLimit(dm, volume));
4624     prerefine = PetscMax(prerefine, 1);
4625   }
4626   if (prerefine) PetscCall(DMLocalizeCoordinates(dm));
4627   for (r = 0; r < prerefine; ++r) {
4628     DM             rdm;
4629     PetscPointFunc coordFunc = ((DM_Plex *)dm->data)->coordFunc;
4630 
4631     PetscCall(DMSetFromOptions_NonRefinement_Plex(dm, PetscOptionsObject));
4632     PetscCall(DMRefine(dm, PetscObjectComm((PetscObject)dm), &rdm));
4633     PetscCall(DMPlexReplace_Internal(dm, &rdm));
4634     PetscCall(DMSetFromOptions_NonRefinement_Plex(dm, PetscOptionsObject));
4635     if (coordFunc && remap) {
4636       PetscCall(DMPlexRemapGeometry(dm, 0.0, coordFunc));
4637       ((DM_Plex *)dm->data)->coordFunc = coordFunc;
4638     }
4639   }
4640   PetscCall(DMPlexSetRefinementUniform(dm, uniformOrig));
4641   /* Handle DMPlex extrusion before distribution */
4642   PetscCall(PetscOptionsBoundedInt("-dm_extrude", "The number of layers to extrude", "", extLayers, &extLayers, NULL, 0));
4643   if (extLayers) {
4644     DM edm;
4645 
4646     PetscCall(DMExtrude(dm, extLayers, &edm));
4647     PetscCall(DMPlexReplace_Internal(dm, &edm));
4648     ((DM_Plex *)dm->data)->coordFunc = NULL;
4649     PetscCall(DMSetFromOptions_NonRefinement_Plex(dm, PetscOptionsObject));
4650     extLayers = 0;
4651     PetscCall(DMGetDimension(dm, &dim));
4652   }
4653   /* Handle DMPlex reordering before distribution */
4654   PetscCall(DMPlexReorderGetDefault(dm, &reorder));
4655   PetscCall(MatGetOrderingList(&ordlist));
4656   PetscCall(PetscStrncpy(oname, MATORDERINGNATURAL, sizeof(oname)));
4657   PetscCall(PetscOptionsFList("-dm_plex_reorder", "Set mesh reordering type", "DMPlexGetOrdering", ordlist, MATORDERINGNATURAL, oname, sizeof(oname), &flg));
4658   if (reorder == DM_REORDER_DEFAULT_TRUE || flg) {
4659     DM pdm;
4660     IS perm;
4661 
4662     PetscCall(DMPlexGetOrdering(dm, oname, NULL, &perm));
4663     PetscCall(DMPlexPermute(dm, perm, &pdm));
4664     PetscCall(ISDestroy(&perm));
4665     PetscCall(DMPlexReplace_Internal(dm, &pdm));
4666     PetscCall(DMSetFromOptions_NonRefinement_Plex(dm, PetscOptionsObject));
4667   }
4668   /* Handle DMPlex distribution */
4669   PetscCall(DMPlexDistributeGetDefault(dm, &distribute));
4670   PetscCall(PetscOptionsBool("-dm_distribute", "Flag to redistribute a mesh among processes", "DMPlexDistribute", distribute, &distribute, NULL));
4671   PetscCall(PetscOptionsBool("-dm_distribute_save_sf", "Flag to save the migration SF", "DMPlexSetMigrationSF", saveSF, &saveSF, NULL));
4672   PetscCall(DMSetFromOptions_Overlap_Plex(dm, PetscOptionsObject, &overlap));
4673   if (distribute) {
4674     DM               pdm = NULL;
4675     PetscPartitioner part;
4676     PetscSF          sfMigration;
4677 
4678     PetscCall(DMPlexGetPartitioner(dm, &part));
4679     PetscCall(PetscPartitionerSetFromOptions(part));
4680     PetscCall(DMPlexDistribute(dm, overlap, &sfMigration, &pdm));
4681     if (pdm) PetscCall(DMPlexReplace_Internal(dm, &pdm));
4682     if (saveSF) PetscCall(DMPlexSetMigrationSF(dm, sfMigration));
4683     PetscCall(PetscSFDestroy(&sfMigration));
4684   }
4685 
4686   {
4687     PetscBool useBoxLabel = PETSC_FALSE;
4688     PetscCall(PetscOptionsBool("-dm_plex_box_label", "Create 'Face Sets' assuming boundary faces align with cartesian directions", "DMCreate", useBoxLabel, &useBoxLabel, NULL));
4689     if (useBoxLabel) PetscCall(DMPlexSetBoxLabel_Internal(dm));
4690   }
4691   /* Must check CEED options before creating function space for coordinates */
4692   {
4693     PetscBool useCeed = PETSC_FALSE, flg;
4694 
4695     PetscCall(PetscOptionsBool("-dm_plex_use_ceed", "Use LibCEED as the FEM backend", "DMPlexSetUseCeed", useCeed, &useCeed, &flg));
4696     if (flg) PetscCall(DMPlexSetUseCeed(dm, useCeed));
4697   }
4698   /* Create coordinate space */
4699   if (created) {
4700     DM_Plex  *mesh   = (DM_Plex *)dm->data;
4701     PetscInt  degree = 1, deg;
4702     PetscInt  height = 0;
4703     DM        cdm;
4704     PetscBool flg, localize = PETSC_TRUE, sparseLocalize = PETSC_TRUE;
4705 
4706     PetscCall(PetscOptionsBool("-dm_coord_space", "Use an FEM space for coordinates", "", coordSpace, &coordSpace, &flg));
4707     PetscCall(PetscOptionsInt("-dm_coord_petscspace_degree", "FEM degree for coordinate space", "", degree, &degree, NULL));
4708     PetscCall(DMGetCoordinateDegree_Internal(dm, &deg));
4709     if (coordSpace && deg <= 1) PetscCall(DMPlexCreateCoordinateSpace(dm, degree, PETSC_TRUE, mesh->coordFunc));
4710     PetscCall(DMGetCoordinateDM(dm, &cdm));
4711     if (flg && !coordSpace) {
4712       PetscDS      cds;
4713       PetscObject  obj;
4714       PetscClassId id;
4715 
4716       PetscCall(DMGetDS(cdm, &cds));
4717       PetscCall(PetscDSGetDiscretization(cds, 0, &obj));
4718       PetscCall(PetscObjectGetClassId(obj, &id));
4719       if (id == PETSCFE_CLASSID) {
4720         PetscContainer dummy;
4721 
4722         PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &dummy));
4723         PetscCall(PetscObjectSetName((PetscObject)dummy, "coordinates"));
4724         PetscCall(DMSetField(cdm, 0, NULL, (PetscObject)dummy));
4725         PetscCall(PetscContainerDestroy(&dummy));
4726         PetscCall(DMClearDS(cdm));
4727       }
4728       mesh->coordFunc = NULL;
4729     }
4730     PetscCall(PetscOptionsBool("-dm_localize", "Localize mesh coordinates", "", localize, &localize, NULL));
4731     PetscCall(PetscOptionsBool("-dm_sparse_localize", "Localize only necessary cells", "DMSetSparseLocalize", sparseLocalize, &sparseLocalize, &flg));
4732     if (flg) PetscCall(DMSetSparseLocalize(dm, sparseLocalize));
4733     PetscCall(PetscOptionsInt("-dm_localize_height", "Localize edges and faces in addition to cells", "", height, &height, &flg));
4734     if (flg) PetscCall(DMPlexSetMaxProjectionHeight(cdm, height));
4735     if (localize) PetscCall(DMLocalizeCoordinates(dm));
4736   }
4737   /* Handle DMPlex refinement */
4738   remap = PETSC_TRUE;
4739   PetscCall(PetscOptionsBoundedInt("-dm_refine", "The number of uniform refinements", "DMCreate", refine, &refine, NULL, 0));
4740   PetscCall(PetscOptionsBool("-dm_refine_remap", "Flag to control coordinate remapping", "DMCreate", remap, &remap, NULL));
4741   PetscCall(PetscOptionsBoundedInt("-dm_refine_hierarchy", "The number of uniform refinements", "DMCreate", refine, &refine, &isHierarchy, 0));
4742   if (refine) PetscCall(DMPlexSetRefinementUniform(dm, PETSC_TRUE));
4743   if (refine && isHierarchy) {
4744     DM *dms, coarseDM;
4745 
4746     PetscCall(DMGetCoarseDM(dm, &coarseDM));
4747     PetscCall(PetscObjectReference((PetscObject)coarseDM));
4748     PetscCall(PetscMalloc1(refine, &dms));
4749     PetscCall(DMRefineHierarchy(dm, refine, dms));
4750     /* Total hack since we do not pass in a pointer */
4751     PetscCall(DMPlexSwap_Static(dm, dms[refine - 1]));
4752     if (refine == 1) {
4753       PetscCall(DMSetCoarseDM(dm, dms[0]));
4754       PetscCall(DMPlexSetRegularRefinement(dm, PETSC_TRUE));
4755     } else {
4756       PetscCall(DMSetCoarseDM(dm, dms[refine - 2]));
4757       PetscCall(DMPlexSetRegularRefinement(dm, PETSC_TRUE));
4758       PetscCall(DMSetCoarseDM(dms[0], dms[refine - 1]));
4759       PetscCall(DMPlexSetRegularRefinement(dms[0], PETSC_TRUE));
4760     }
4761     PetscCall(DMSetCoarseDM(dms[refine - 1], coarseDM));
4762     PetscCall(PetscObjectDereference((PetscObject)coarseDM));
4763     /* Free DMs */
4764     for (r = 0; r < refine; ++r) {
4765       PetscCall(DMSetFromOptions_NonRefinement_Plex(dms[r], PetscOptionsObject));
4766       PetscCall(DMDestroy(&dms[r]));
4767     }
4768     PetscCall(PetscFree(dms));
4769   } else {
4770     for (r = 0; r < refine; ++r) {
4771       DM             rdm;
4772       PetscPointFunc coordFunc = ((DM_Plex *)dm->data)->coordFunc;
4773 
4774       PetscCall(DMSetFromOptions_NonRefinement_Plex(dm, PetscOptionsObject));
4775       PetscCall(DMRefine(dm, PetscObjectComm((PetscObject)dm), &rdm));
4776       /* Total hack since we do not pass in a pointer */
4777       PetscCall(DMPlexReplace_Internal(dm, &rdm));
4778       PetscCall(DMSetFromOptions_NonRefinement_Plex(dm, PetscOptionsObject));
4779       if (coordFunc && remap) {
4780         PetscCall(DMPlexRemapGeometry(dm, 0.0, coordFunc));
4781         ((DM_Plex *)dm->data)->coordFunc = coordFunc;
4782       }
4783     }
4784   }
4785   /* Handle DMPlex coarsening */
4786   PetscCall(PetscOptionsBoundedInt("-dm_coarsen", "Coarsen the mesh", "DMCreate", coarsen, &coarsen, NULL, 0));
4787   PetscCall(PetscOptionsBoundedInt("-dm_coarsen_hierarchy", "The number of coarsenings", "DMCreate", coarsen, &coarsen, &isHierarchy, 0));
4788   if (coarsen && isHierarchy) {
4789     DM *dms;
4790 
4791     PetscCall(PetscMalloc1(coarsen, &dms));
4792     PetscCall(DMCoarsenHierarchy(dm, coarsen, dms));
4793     /* Free DMs */
4794     for (r = 0; r < coarsen; ++r) {
4795       PetscCall(DMSetFromOptions_NonRefinement_Plex(dms[r], PetscOptionsObject));
4796       PetscCall(DMDestroy(&dms[r]));
4797     }
4798     PetscCall(PetscFree(dms));
4799   } else {
4800     for (r = 0; r < coarsen; ++r) {
4801       DM             cdm;
4802       PetscPointFunc coordFunc = ((DM_Plex *)dm->data)->coordFunc;
4803 
4804       PetscCall(DMSetFromOptions_NonRefinement_Plex(dm, PetscOptionsObject));
4805       PetscCall(DMCoarsen(dm, PetscObjectComm((PetscObject)dm), &cdm));
4806       /* Total hack since we do not pass in a pointer */
4807       PetscCall(DMPlexReplace_Internal(dm, &cdm));
4808       PetscCall(DMSetFromOptions_NonRefinement_Plex(dm, PetscOptionsObject));
4809       if (coordFunc) {
4810         PetscCall(DMPlexRemapGeometry(dm, 0.0, coordFunc));
4811         ((DM_Plex *)dm->data)->coordFunc = coordFunc;
4812       }
4813     }
4814   }
4815   // Handle coordinate remapping
4816   remap = PETSC_FALSE;
4817   PetscCall(PetscOptionsBool("-dm_coord_remap", "Flag to control coordinate remapping", "", remap, &remap, NULL));
4818   if (remap) {
4819     DMPlexCoordMap map     = DM_COORD_MAP_NONE;
4820     PetscPointFunc mapFunc = NULL;
4821     PetscScalar    params[16];
4822     PetscInt       Np = PETSC_STATIC_ARRAY_LENGTH(params), cdim;
4823     MPI_Comm       comm;
4824 
4825     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
4826     PetscCall(DMGetCoordinateDim(dm, &cdim));
4827     PetscCall(PetscOptionsScalarArray("-dm_coord_map_params", "Parameters for the coordinate remapping", "", params, &Np, &flg));
4828     if (!flg) Np = 0;
4829     // TODO Allow user to pass a map function by name
4830     PetscCall(PetscOptionsEnum("-dm_coord_map", "Coordinate mapping for built-in mesh", "", DMPlexCoordMaps, (PetscEnum)map, (PetscEnum *)&map, &flg));
4831     if (flg) {
4832       switch (map) {
4833       case DM_COORD_MAP_NONE:
4834         mapFunc = coordMap_identity;
4835         break;
4836       case DM_COORD_MAP_SHEAR:
4837         mapFunc = coordMap_shear;
4838         if (!Np) {
4839           Np        = cdim + 1;
4840           params[0] = 0;
4841           for (PetscInt d = 1; d <= cdim; ++d) params[d] = 1.0;
4842         }
4843         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);
4844         break;
4845       case DM_COORD_MAP_FLARE:
4846         mapFunc = coordMap_flare;
4847         if (!Np) {
4848           Np        = cdim + 1;
4849           params[0] = 0;
4850           for (PetscInt d = 1; d <= cdim; ++d) params[d] = 1.0;
4851         }
4852         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);
4853         break;
4854       case DM_COORD_MAP_ANNULUS:
4855         mapFunc = coordMap_annulus;
4856         if (!Np) {
4857           Np        = 2;
4858           params[0] = 1.;
4859           params[1] = 2.;
4860         }
4861         PetscCheck(Np == 2, comm, PETSC_ERR_ARG_WRONG, "The annulus coordinate map must have 2 parameters, not %" PetscInt_FMT, Np);
4862         break;
4863       case DM_COORD_MAP_SHELL:
4864         mapFunc = coordMap_shell;
4865         if (!Np) {
4866           Np        = 2;
4867           params[0] = 1.;
4868           params[1] = 2.;
4869         }
4870         PetscCheck(Np == 2, comm, PETSC_ERR_ARG_WRONG, "The spherical shell coordinate map must have 2 parameters, not %" PetscInt_FMT, Np);
4871         break;
4872       default:
4873         mapFunc = coordMap_identity;
4874       }
4875     }
4876     if (Np) {
4877       DM      cdm;
4878       PetscDS cds;
4879 
4880       PetscCall(DMGetCoordinateDM(dm, &cdm));
4881       PetscCall(DMGetDS(cdm, &cds));
4882       PetscCall(PetscDSSetConstants(cds, Np, params));
4883     }
4884     PetscCall(DMPlexRemapGeometry(dm, 0.0, mapFunc));
4885   }
4886   /* Handle ghost cells */
4887   PetscCall(PetscOptionsBool("-dm_plex_create_fv_ghost_cells", "Flag to create finite volume ghost cells on the boundary", "DMCreate", ghostCells, &ghostCells, NULL));
4888   if (ghostCells) {
4889     DM   gdm;
4890     char lname[PETSC_MAX_PATH_LEN];
4891 
4892     lname[0] = '\0';
4893     PetscCall(PetscOptionsString("-dm_plex_fv_ghost_cells_label", "Label name for ghost cells boundary", "DMCreate", lname, lname, sizeof(lname), &flg));
4894     PetscCall(DMPlexConstructGhostCells(dm, flg ? lname : NULL, NULL, &gdm));
4895     PetscCall(DMPlexReplace_Internal(dm, &gdm));
4896   }
4897   /* Handle 1D order */
4898   if (reorder != DM_REORDER_DEFAULT_FALSE && dim == 1) {
4899     DM           cdm, rdm;
4900     PetscDS      cds;
4901     PetscObject  obj;
4902     PetscClassId id = PETSC_OBJECT_CLASSID;
4903     IS           perm;
4904     PetscInt     Nf;
4905     PetscBool    distributed;
4906 
4907     PetscCall(DMPlexIsDistributed(dm, &distributed));
4908     PetscCall(DMGetCoordinateDM(dm, &cdm));
4909     PetscCall(DMGetDS(cdm, &cds));
4910     PetscCall(PetscDSGetNumFields(cds, &Nf));
4911     if (Nf) {
4912       PetscCall(PetscDSGetDiscretization(cds, 0, &obj));
4913       PetscCall(PetscObjectGetClassId(obj, &id));
4914     }
4915     if (!distributed && id != PETSCFE_CLASSID) {
4916       PetscCall(DMPlexGetOrdering1D(dm, &perm));
4917       PetscCall(DMPlexPermute(dm, perm, &rdm));
4918       PetscCall(DMPlexReplace_Internal(dm, &rdm));
4919       PetscCall(ISDestroy(&perm));
4920     }
4921   }
4922 /* Handle */
4923 non_refine:
4924   PetscCall(DMSetFromOptions_NonRefinement_Plex(dm, PetscOptionsObject));
4925   char    *phases[16];
4926   PetscInt Nphases = 16;
4927   PetscCall(PetscOptionsStringArray("-dm_plex_option_phases", "Option phase prefixes", "DMSetFromOptions", phases, &Nphases, &flg));
4928   PetscOptionsHeadEnd();
4929 
4930   // Phases
4931   if (flg) {
4932     const char *oldPrefix;
4933 
4934     PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &oldPrefix));
4935     for (PetscInt ph = 0; ph < Nphases; ++ph) {
4936       PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)dm, phases[ph]));
4937       PetscCall(PetscInfo(dm, "Options phase %s for DM %s\n", phases[ph], dm->hdr.name));
4938       PetscCall(DMSetFromOptions(dm));
4939       PetscCall(PetscObjectSetOptionsPrefix((PetscObject)dm, oldPrefix));
4940       PetscCall(PetscFree(phases[ph]));
4941     }
4942   }
4943   PetscFunctionReturn(PETSC_SUCCESS);
4944 }
4945 
4946 static PetscErrorCode DMCreateGlobalVector_Plex(DM dm, Vec *vec)
4947 {
4948   PetscFunctionBegin;
4949   PetscCall(DMCreateGlobalVector_Section_Private(dm, vec));
4950   /* PetscCall(VecSetOperation(*vec, VECOP_DUPLICATE, (void(*)(void)) VecDuplicate_MPI_DM)); */
4951   PetscCall(VecSetOperation(*vec, VECOP_VIEW, (void (*)(void))VecView_Plex));
4952   PetscCall(VecSetOperation(*vec, VECOP_VIEWNATIVE, (void (*)(void))VecView_Plex_Native));
4953   PetscCall(VecSetOperation(*vec, VECOP_LOAD, (void (*)(void))VecLoad_Plex));
4954   PetscCall(VecSetOperation(*vec, VECOP_LOADNATIVE, (void (*)(void))VecLoad_Plex_Native));
4955   PetscFunctionReturn(PETSC_SUCCESS);
4956 }
4957 
4958 static PetscErrorCode DMCreateLocalVector_Plex(DM dm, Vec *vec)
4959 {
4960   PetscFunctionBegin;
4961   PetscCall(DMCreateLocalVector_Section_Private(dm, vec));
4962   PetscCall(VecSetOperation(*vec, VECOP_VIEW, (void (*)(void))VecView_Plex_Local));
4963   PetscCall(VecSetOperation(*vec, VECOP_LOAD, (void (*)(void))VecLoad_Plex_Local));
4964   PetscFunctionReturn(PETSC_SUCCESS);
4965 }
4966 
4967 static PetscErrorCode DMGetDimPoints_Plex(DM dm, PetscInt dim, PetscInt *pStart, PetscInt *pEnd)
4968 {
4969   PetscInt depth, d;
4970 
4971   PetscFunctionBegin;
4972   PetscCall(DMPlexGetDepth(dm, &depth));
4973   if (depth == 1) {
4974     PetscCall(DMGetDimension(dm, &d));
4975     if (dim == 0) PetscCall(DMPlexGetDepthStratum(dm, dim, pStart, pEnd));
4976     else if (dim == d) PetscCall(DMPlexGetDepthStratum(dm, 1, pStart, pEnd));
4977     else {
4978       *pStart = 0;
4979       *pEnd   = 0;
4980     }
4981   } else {
4982     PetscCall(DMPlexGetDepthStratum(dm, dim, pStart, pEnd));
4983   }
4984   PetscFunctionReturn(PETSC_SUCCESS);
4985 }
4986 
4987 static PetscErrorCode DMGetNeighbors_Plex(DM dm, PetscInt *nranks, const PetscMPIInt *ranks[])
4988 {
4989   PetscSF            sf;
4990   PetscMPIInt        niranks, njranks;
4991   PetscInt           n;
4992   const PetscMPIInt *iranks, *jranks;
4993   DM_Plex           *data = (DM_Plex *)dm->data;
4994 
4995   PetscFunctionBegin;
4996   PetscCall(DMGetPointSF(dm, &sf));
4997   if (!data->neighbors) {
4998     PetscCall(PetscSFSetUp(sf));
4999     PetscCall(PetscSFGetRootRanks(sf, &njranks, &jranks, NULL, NULL, NULL));
5000     PetscCall(PetscSFGetLeafRanks(sf, &niranks, &iranks, NULL, NULL));
5001     PetscCall(PetscMalloc1(njranks + niranks + 1, &data->neighbors));
5002     PetscCall(PetscArraycpy(data->neighbors + 1, jranks, njranks));
5003     PetscCall(PetscArraycpy(data->neighbors + njranks + 1, iranks, niranks));
5004     n = njranks + niranks;
5005     PetscCall(PetscSortRemoveDupsMPIInt(&n, data->neighbors + 1));
5006     /* The following cast should never fail: can't have more neighbors than PETSC_MPI_INT_MAX */
5007     PetscCall(PetscMPIIntCast(n, data->neighbors));
5008   }
5009   if (nranks) *nranks = data->neighbors[0];
5010   if (ranks) {
5011     if (data->neighbors[0]) *ranks = data->neighbors + 1;
5012     else *ranks = NULL;
5013   }
5014   PetscFunctionReturn(PETSC_SUCCESS);
5015 }
5016 
5017 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM, DM, Mat, Vec, Vec);
5018 
5019 static PetscErrorCode DMInitialize_Plex(DM dm)
5020 {
5021   PetscFunctionBegin;
5022   dm->ops->view                      = DMView_Plex;
5023   dm->ops->load                      = DMLoad_Plex;
5024   dm->ops->setfromoptions            = DMSetFromOptions_Plex;
5025   dm->ops->clone                     = DMClone_Plex;
5026   dm->ops->setup                     = DMSetUp_Plex;
5027   dm->ops->createlocalsection        = DMCreateLocalSection_Plex;
5028   dm->ops->createsectionpermutation  = DMCreateSectionPermutation_Plex;
5029   dm->ops->createdefaultconstraints  = DMCreateDefaultConstraints_Plex;
5030   dm->ops->createglobalvector        = DMCreateGlobalVector_Plex;
5031   dm->ops->createlocalvector         = DMCreateLocalVector_Plex;
5032   dm->ops->getlocaltoglobalmapping   = NULL;
5033   dm->ops->createfieldis             = NULL;
5034   dm->ops->createcoordinatedm        = DMCreateCoordinateDM_Plex;
5035   dm->ops->createcoordinatefield     = DMCreateCoordinateField_Plex;
5036   dm->ops->getcoloring               = NULL;
5037   dm->ops->creatematrix              = DMCreateMatrix_Plex;
5038   dm->ops->createinterpolation       = DMCreateInterpolation_Plex;
5039   dm->ops->createmassmatrix          = DMCreateMassMatrix_Plex;
5040   dm->ops->createmassmatrixlumped    = DMCreateMassMatrixLumped_Plex;
5041   dm->ops->createinjection           = DMCreateInjection_Plex;
5042   dm->ops->refine                    = DMRefine_Plex;
5043   dm->ops->coarsen                   = DMCoarsen_Plex;
5044   dm->ops->refinehierarchy           = DMRefineHierarchy_Plex;
5045   dm->ops->coarsenhierarchy          = DMCoarsenHierarchy_Plex;
5046   dm->ops->extrude                   = DMExtrude_Plex;
5047   dm->ops->globaltolocalbegin        = NULL;
5048   dm->ops->globaltolocalend          = NULL;
5049   dm->ops->localtoglobalbegin        = NULL;
5050   dm->ops->localtoglobalend          = NULL;
5051   dm->ops->destroy                   = DMDestroy_Plex;
5052   dm->ops->createsubdm               = DMCreateSubDM_Plex;
5053   dm->ops->createsuperdm             = DMCreateSuperDM_Plex;
5054   dm->ops->getdimpoints              = DMGetDimPoints_Plex;
5055   dm->ops->locatepoints              = DMLocatePoints_Plex;
5056   dm->ops->projectfunctionlocal      = DMProjectFunctionLocal_Plex;
5057   dm->ops->projectfunctionlabellocal = DMProjectFunctionLabelLocal_Plex;
5058   dm->ops->projectfieldlocal         = DMProjectFieldLocal_Plex;
5059   dm->ops->projectfieldlabellocal    = DMProjectFieldLabelLocal_Plex;
5060   dm->ops->projectbdfieldlabellocal  = DMProjectBdFieldLabelLocal_Plex;
5061   dm->ops->computel2diff             = DMComputeL2Diff_Plex;
5062   dm->ops->computel2gradientdiff     = DMComputeL2GradientDiff_Plex;
5063   dm->ops->computel2fielddiff        = DMComputeL2FieldDiff_Plex;
5064   dm->ops->getneighbors              = DMGetNeighbors_Plex;
5065   dm->ops->getlocalboundingbox       = DMGetLocalBoundingBox_Coordinates;
5066   dm->ops->createdomaindecomposition = DMCreateDomainDecomposition_Plex;
5067   dm->ops->createddscatters          = DMCreateDomainDecompositionScatters_Plex;
5068   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", DMPlexInsertBoundaryValues_Plex));
5069   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerivativeBoundaryValues_C", DMPlexInsertTimeDerivativeBoundaryValues_Plex));
5070   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", DMSetUpGLVisViewer_Plex));
5071   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", DMCreateNeumannOverlap_Plex));
5072   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", DMPlexDistributeGetDefault_Plex));
5073   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", DMPlexDistributeSetDefault_Plex));
5074   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", DMPlexReorderGetDefault_Plex));
5075   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", DMPlexReorderSetDefault_Plex));
5076   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetDefault_C", DMReorderSectionGetDefault_Plex));
5077   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetDefault_C", DMReorderSectionSetDefault_Plex));
5078   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetType_C", DMReorderSectionGetType_Plex));
5079   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetType_C", DMReorderSectionSetType_Plex));
5080   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", DMInterpolateSolution_Plex));
5081   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", DMPlexGetOverlap_Plex));
5082   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", DMPlexSetOverlap_Plex));
5083   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetUseCeed_C", DMPlexGetUseCeed_Plex));
5084   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetUseCeed_C", DMPlexSetUseCeed_Plex));
5085   PetscFunctionReturn(PETSC_SUCCESS);
5086 }
5087 
5088 PETSC_INTERN PetscErrorCode DMClone_Plex(DM dm, DM *newdm)
5089 {
5090   DM_Plex       *mesh = (DM_Plex *)dm->data;
5091   const PetscSF *face_sfs;
5092   PetscInt       num_face_sfs;
5093 
5094   PetscFunctionBegin;
5095   mesh->refct++;
5096   (*newdm)->data = mesh;
5097   PetscCall(DMPlexGetIsoperiodicFaceSF(dm, &num_face_sfs, &face_sfs));
5098   PetscCall(DMPlexSetIsoperiodicFaceSF(*newdm, num_face_sfs, (PetscSF *)face_sfs));
5099   PetscCall(PetscObjectChangeTypeName((PetscObject)*newdm, DMPLEX));
5100   PetscCall(DMInitialize_Plex(*newdm));
5101   PetscFunctionReturn(PETSC_SUCCESS);
5102 }
5103 
5104 /*MC
5105   DMPLEX = "plex" - A `DM` object that encapsulates an unstructured mesh, or CW Complex, which can be expressed using a Hasse Diagram.
5106                     In the local representation, `Vec`s contain all unknowns in the interior and shared boundary. This is
5107                     specified by a PetscSection object. Ownership in the global representation is determined by
5108                     ownership of the underlying `DMPLEX` points. This is specified by another `PetscSection` object.
5109 
5110   Options Database Keys:
5111 + -dm_refine_pre                     - Refine mesh before distribution
5112 + -dm_refine_uniform_pre             - Choose uniform or generator-based refinement
5113 + -dm_refine_volume_limit_pre        - Cell volume limit after pre-refinement using generator
5114 . -dm_distribute                     - Distribute mesh across processes
5115 . -dm_distribute_overlap             - Number of cells to overlap for distribution
5116 . -dm_refine                         - Refine mesh after distribution
5117 . -dm_localize <bool>                - Whether to localize coordinates for periodic meshes
5118 . -dm_sparse_localize <bool>         - Whether to only localize cells on the periodic boundary
5119 . -dm_plex_hash_location             - Use grid hashing for point location
5120 . -dm_plex_hash_box_faces <n,m,p>    - The number of divisions in each direction of the grid hash
5121 . -dm_plex_partition_balance         - Attempt to evenly divide points on partition boundary between processes
5122 . -dm_plex_remesh_bd                 - Allow changes to the boundary on remeshing
5123 . -dm_plex_max_projection_height     - Maximum mesh point height used to project locally
5124 . -dm_plex_regular_refinement        - Use special nested projection algorithm for regular refinement
5125 . -dm_plex_reorder_section           - Use specialized blocking if available
5126 . -dm_plex_check_all                 - Perform all checks below
5127 . -dm_plex_check_symmetry            - Check that the adjacency information in the mesh is symmetric
5128 . -dm_plex_check_skeleton <celltype> - Check that each cell has the correct number of vertices
5129 . -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
5130 . -dm_plex_check_geometry            - Check that cells have positive volume
5131 . -dm_view :mesh.tex:ascii_latex     - View the mesh in LaTeX/TikZ
5132 . -dm_plex_view_scale <num>          - Scale the TikZ
5133 . -dm_plex_print_fem <num>           - View FEM assembly information, such as element vectors and matrices
5134 - -dm_plex_print_fvm <num>           - View FVM assembly information, such as flux updates
5135 
5136   Level: intermediate
5137 
5138 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMType`, `DMPlexCreate()`, `DMCreate()`, `DMSetType()`, `PetscSection`
5139 M*/
5140 
5141 PETSC_EXTERN PetscErrorCode DMCreate_Plex(DM dm)
5142 {
5143   DM_Plex *mesh;
5144   PetscInt unit;
5145 
5146   PetscFunctionBegin;
5147   PetscCall(PetscCitationsRegister(PlexCitation, &Plexcite));
5148   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5149   PetscCall(PetscNew(&mesh));
5150   dm->reorderSection = DM_REORDER_DEFAULT_NOTSET;
5151   dm->data           = mesh;
5152 
5153   mesh->refct = 1;
5154   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &mesh->coneSection));
5155   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &mesh->supportSection));
5156   mesh->refinementUniform      = PETSC_TRUE;
5157   mesh->refinementLimit        = -1.0;
5158   mesh->distDefault            = PETSC_TRUE;
5159   mesh->reorderDefault         = DM_REORDER_DEFAULT_NOTSET;
5160   mesh->distributionName       = NULL;
5161   mesh->interpolated           = DMPLEX_INTERPOLATED_INVALID;
5162   mesh->interpolatedCollective = DMPLEX_INTERPOLATED_INVALID;
5163 
5164   PetscCall(PetscPartitionerCreate(PetscObjectComm((PetscObject)dm), &mesh->partitioner));
5165   mesh->remeshBd = PETSC_FALSE;
5166 
5167   for (unit = 0; unit < NUM_PETSC_UNITS; ++unit) mesh->scale[unit] = 1.0;
5168 
5169   mesh->depthState    = -1;
5170   mesh->celltypeState = -1;
5171   mesh->printTol      = 1.0e-10;
5172   mesh->nonempty_comm = MPI_COMM_SELF;
5173 
5174   PetscCall(DMInitialize_Plex(dm));
5175   PetscFunctionReturn(PETSC_SUCCESS);
5176 }
5177 
5178 /*@
5179   DMPlexCreate - Creates a `DMPLEX` object, which encapsulates an unstructured mesh, or CW complex, which can be expressed using a Hasse Diagram.
5180 
5181   Collective
5182 
5183   Input Parameter:
5184 . comm - The communicator for the `DMPLEX` object
5185 
5186   Output Parameter:
5187 . mesh - The `DMPLEX` object
5188 
5189   Level: beginner
5190 
5191 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMType`, `DMCreate()`, `DMSetType()`
5192 @*/
5193 PetscErrorCode DMPlexCreate(MPI_Comm comm, DM *mesh)
5194 {
5195   PetscFunctionBegin;
5196   PetscAssertPointer(mesh, 2);
5197   PetscCall(DMCreate(comm, mesh));
5198   PetscCall(DMSetType(*mesh, DMPLEX));
5199   PetscFunctionReturn(PETSC_SUCCESS);
5200 }
5201 
5202 /*@C
5203   DMPlexBuildFromCellListParallel - Build distributed `DMPLEX` topology from a list of vertices for each cell (common mesh generator output) where all cells have the same celltype
5204 
5205   Collective; No Fortran Support
5206 
5207   Input Parameters:
5208 + dm          - The `DM`
5209 . numCells    - The number of cells owned by this process
5210 . numVertices - The number of vertices to be owned by this process, or `PETSC_DECIDE`
5211 . NVertices   - The global number of vertices, or `PETSC_DETERMINE`
5212 . numCorners  - The number of vertices for each cell
5213 - cells       - An array of numCells*numCorners numbers, the global vertex numbers for each cell
5214 
5215   Output Parameters:
5216 + vertexSF         - (Optional) `PetscSF` describing complete vertex ownership
5217 - verticesAdjSaved - (Optional) vertex adjacency array
5218 
5219   Level: advanced
5220 
5221   Notes:
5222   Two triangles sharing a face
5223 .vb
5224 
5225         2
5226       / | \
5227      /  |  \
5228     /   |   \
5229    0  0 | 1  3
5230     \   |   /
5231      \  |  /
5232       \ | /
5233         1
5234 .ve
5235   would have input
5236 .vb
5237   numCells = 2, numVertices = 4
5238   cells = [0 1 2  1 3 2]
5239 .ve
5240   which would result in the `DMPLEX`
5241 .vb
5242 
5243         4
5244       / | \
5245      /  |  \
5246     /   |   \
5247    2  0 | 1  5
5248     \   |   /
5249      \  |  /
5250       \ | /
5251         3
5252 .ve
5253 
5254   Vertices are implicitly numbered consecutively 0,...,NVertices.
5255   Each rank owns a chunk of numVertices consecutive vertices.
5256   If numVertices is `PETSC_DECIDE`, PETSc will distribute them as evenly as possible using PetscLayout.
5257   If NVertices is `PETSC_DETERMINE` and numVertices is PETSC_DECIDE, NVertices is computed by PETSc as the maximum vertex index in cells + 1.
5258   If only NVertices is `PETSC_DETERMINE`, it is computed as the sum of numVertices over all ranks.
5259 
5260   The cell distribution is arbitrary non-overlapping, independent of the vertex distribution.
5261 
5262 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexBuildFromCellList()`, `DMPlexCreateFromCellListParallelPetsc()`, `DMPlexBuildCoordinatesFromCellListParallel()`,
5263           `PetscSF`
5264 @*/
5265 PetscErrorCode DMPlexBuildFromCellListParallel(DM dm, PetscInt numCells, PetscInt numVertices, PetscInt NVertices, PetscInt numCorners, const PetscInt cells[], PetscSF *vertexSF, PetscInt **verticesAdjSaved)
5266 {
5267   PetscSF     sfPoint;
5268   PetscLayout layout;
5269   PetscInt    numVerticesAdj, *verticesAdj, *cones, c, p;
5270 
5271   PetscFunctionBegin;
5272   PetscValidLogicalCollectiveInt(dm, NVertices, 4);
5273   PetscCall(PetscLogEventBegin(DMPLEX_BuildFromCellList, dm, 0, 0, 0));
5274   /* Get/check global number of vertices */
5275   {
5276     PetscInt       NVerticesInCells, i;
5277     const PetscInt len = numCells * numCorners;
5278 
5279     /* NVerticesInCells = max(cells) + 1 */
5280     NVerticesInCells = PETSC_INT_MIN;
5281     for (i = 0; i < len; i++)
5282       if (cells[i] > NVerticesInCells) NVerticesInCells = cells[i];
5283     ++NVerticesInCells;
5284     PetscCallMPI(MPIU_Allreduce(MPI_IN_PLACE, &NVerticesInCells, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
5285 
5286     if (numVertices == PETSC_DECIDE && NVertices == PETSC_DECIDE) NVertices = NVerticesInCells;
5287     else
5288       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);
5289   }
5290   /* Count locally unique vertices */
5291   {
5292     PetscHSetI vhash;
5293     PetscInt   off = 0;
5294 
5295     PetscCall(PetscHSetICreate(&vhash));
5296     for (c = 0; c < numCells; ++c) {
5297       for (p = 0; p < numCorners; ++p) PetscCall(PetscHSetIAdd(vhash, cells[c * numCorners + p]));
5298     }
5299     PetscCall(PetscHSetIGetSize(vhash, &numVerticesAdj));
5300     if (!verticesAdjSaved) PetscCall(PetscMalloc1(numVerticesAdj, &verticesAdj));
5301     else verticesAdj = *verticesAdjSaved;
5302     PetscCall(PetscHSetIGetElems(vhash, &off, verticesAdj));
5303     PetscCall(PetscHSetIDestroy(&vhash));
5304     PetscCheck(off == numVerticesAdj, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Invalid number of local vertices %" PetscInt_FMT " should be %" PetscInt_FMT, off, numVerticesAdj);
5305   }
5306   PetscCall(PetscSortInt(numVerticesAdj, verticesAdj));
5307   /* Create cones */
5308   PetscCall(DMPlexSetChart(dm, 0, numCells + numVerticesAdj));
5309   for (c = 0; c < numCells; ++c) PetscCall(DMPlexSetConeSize(dm, c, numCorners));
5310   PetscCall(DMSetUp(dm));
5311   PetscCall(DMPlexGetCones(dm, &cones));
5312   for (c = 0; c < numCells; ++c) {
5313     for (p = 0; p < numCorners; ++p) {
5314       const PetscInt gv = cells[c * numCorners + p];
5315       PetscInt       lv;
5316 
5317       /* Positions within verticesAdj form 0-based local vertex numbering;
5318          we need to shift it by numCells to get correct DAG points (cells go first) */
5319       PetscCall(PetscFindInt(gv, numVerticesAdj, verticesAdj, &lv));
5320       PetscCheck(lv >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Could not find global vertex %" PetscInt_FMT " in local connectivity", gv);
5321       cones[c * numCorners + p] = lv + numCells;
5322     }
5323   }
5324   /* Build point sf */
5325   PetscCall(PetscLayoutCreate(PetscObjectComm((PetscObject)dm), &layout));
5326   PetscCall(PetscLayoutSetSize(layout, NVertices));
5327   PetscCall(PetscLayoutSetLocalSize(layout, numVertices));
5328   PetscCall(PetscLayoutSetBlockSize(layout, 1));
5329   PetscCall(PetscSFCreateByMatchingIndices(layout, numVerticesAdj, verticesAdj, NULL, numCells, numVerticesAdj, verticesAdj, NULL, numCells, vertexSF, &sfPoint));
5330   PetscCall(PetscLayoutDestroy(&layout));
5331   if (!verticesAdjSaved) PetscCall(PetscFree(verticesAdj));
5332   PetscCall(PetscObjectSetName((PetscObject)sfPoint, "point SF"));
5333   if (dm->sf) {
5334     const char *prefix;
5335 
5336     PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm->sf, &prefix));
5337     PetscCall(PetscObjectSetOptionsPrefix((PetscObject)sfPoint, prefix));
5338   }
5339   PetscCall(DMSetPointSF(dm, sfPoint));
5340   PetscCall(PetscSFDestroy(&sfPoint));
5341   if (vertexSF) PetscCall(PetscObjectSetName((PetscObject)*vertexSF, "Vertex Ownership SF"));
5342   /* Fill in the rest of the topology structure */
5343   PetscCall(DMPlexSymmetrize(dm));
5344   PetscCall(DMPlexStratify(dm));
5345   PetscCall(PetscLogEventEnd(DMPLEX_BuildFromCellList, dm, 0, 0, 0));
5346   PetscFunctionReturn(PETSC_SUCCESS);
5347 }
5348 
5349 /*@C
5350   DMPlexBuildFromCellSectionParallel - Build distributed `DMPLEX` topology from a list of vertices for each cell (common mesh generator output) allowing multiple celltypes
5351 
5352   Collective; No Fortran Support
5353 
5354   Input Parameters:
5355 + dm          - The `DM`
5356 . numCells    - The number of cells owned by this process
5357 . numVertices - The number of vertices to be owned by this process, or `PETSC_DECIDE`
5358 . NVertices   - The global number of vertices, or `PETSC_DETERMINE`
5359 . cellSection - The `PetscSection` giving the number of vertices for each cell (layout of cells)
5360 - cells       - An array of the global vertex numbers for each cell
5361 
5362   Output Parameters:
5363 + vertexSF         - (Optional) `PetscSF` describing complete vertex ownership
5364 - verticesAdjSaved - (Optional) vertex adjacency array
5365 
5366   Level: advanced
5367 
5368   Notes:
5369   A triangle and quadrilateral sharing a face
5370 .vb
5371         2----------3
5372       / |          |
5373      /  |          |
5374     /   |          |
5375    0  0 |     1    |
5376     \   |          |
5377      \  |          |
5378       \ |          |
5379         1----------4
5380 .ve
5381   would have input
5382 .vb
5383   numCells = 2, numVertices = 5
5384   cells = [0 1 2  1 4 3 2]
5385 .ve
5386   which would result in the `DMPLEX`
5387 .vb
5388         4----------5
5389       / |          |
5390      /  |          |
5391     /   |          |
5392    2  0 |     1    |
5393     \   |          |
5394      \  |          |
5395       \ |          |
5396         3----------6
5397 .ve
5398 
5399   Vertices are implicitly numbered consecutively 0,...,NVertices.
5400   Each rank owns a chunk of numVertices consecutive vertices.
5401   If numVertices is `PETSC_DECIDE`, PETSc will distribute them as evenly as possible using PetscLayout.
5402   If NVertices is `PETSC_DETERMINE` and numVertices is PETSC_DECIDE, NVertices is computed by PETSc as the maximum vertex index in cells + 1.
5403   If only NVertices is `PETSC_DETERMINE`, it is computed as the sum of numVertices over all ranks.
5404 
5405   The cell distribution is arbitrary non-overlapping, independent of the vertex distribution.
5406 
5407 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexBuildFromCellListParallel()`, `DMPlexCreateFromCellSectionParallel()`, `DMPlexBuildCoordinatesFromCellListParallel()`,
5408           `PetscSF`
5409 @*/
5410 PetscErrorCode DMPlexBuildFromCellSectionParallel(DM dm, PetscInt numCells, PetscInt numVertices, PetscInt NVertices, PetscSection cellSection, const PetscInt cells[], PetscSF *vertexSF, PetscInt **verticesAdjSaved)
5411 {
5412   PetscSF     sfPoint;
5413   PetscLayout layout;
5414   PetscInt    numVerticesAdj, *verticesAdj, *cones, cStart, cEnd, len;
5415 
5416   PetscFunctionBegin;
5417   PetscValidLogicalCollectiveInt(dm, NVertices, 4);
5418   PetscCall(PetscLogEventBegin(DMPLEX_BuildFromCellList, dm, 0, 0, 0));
5419   PetscCall(PetscSectionGetChart(cellSection, &cStart, &cEnd));
5420   PetscCall(PetscSectionGetStorageSize(cellSection, &len));
5421   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);
5422   /* Get/check global number of vertices */
5423   {
5424     PetscInt NVerticesInCells;
5425 
5426     /* NVerticesInCells = max(cells) + 1 */
5427     NVerticesInCells = PETSC_MIN_INT;
5428     for (PetscInt i = 0; i < len; i++)
5429       if (cells[i] > NVerticesInCells) NVerticesInCells = cells[i];
5430     ++NVerticesInCells;
5431     PetscCallMPI(MPIU_Allreduce(MPI_IN_PLACE, &NVerticesInCells, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
5432 
5433     if (numVertices == PETSC_DECIDE && NVertices == PETSC_DECIDE) NVertices = NVerticesInCells;
5434     else
5435       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);
5436   }
5437   /* Count locally unique vertices */
5438   {
5439     PetscHSetI vhash;
5440     PetscInt   off = 0;
5441 
5442     PetscCall(PetscHSetICreate(&vhash));
5443     for (PetscInt i = 0; i < len; i++) PetscCall(PetscHSetIAdd(vhash, cells[i]));
5444     PetscCall(PetscHSetIGetSize(vhash, &numVerticesAdj));
5445     if (!verticesAdjSaved) PetscCall(PetscMalloc1(numVerticesAdj, &verticesAdj));
5446     else verticesAdj = *verticesAdjSaved;
5447     PetscCall(PetscHSetIGetElems(vhash, &off, verticesAdj));
5448     PetscCall(PetscHSetIDestroy(&vhash));
5449     PetscCheck(off == numVerticesAdj, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Invalid number of local vertices %" PetscInt_FMT " should be %" PetscInt_FMT, off, numVerticesAdj);
5450   }
5451   PetscCall(PetscSortInt(numVerticesAdj, verticesAdj));
5452   /* Create cones */
5453   PetscCall(DMPlexSetChart(dm, 0, numCells + numVerticesAdj));
5454   for (PetscInt c = 0; c < numCells; ++c) {
5455     PetscInt dof;
5456 
5457     PetscCall(PetscSectionGetDof(cellSection, c, &dof));
5458     PetscCall(DMPlexSetConeSize(dm, c, dof));
5459   }
5460   PetscCall(DMSetUp(dm));
5461   PetscCall(DMPlexGetCones(dm, &cones));
5462   for (PetscInt c = 0; c < numCells; ++c) {
5463     PetscInt dof, off;
5464 
5465     PetscCall(PetscSectionGetDof(cellSection, c, &dof));
5466     PetscCall(PetscSectionGetOffset(cellSection, c, &off));
5467     for (PetscInt p = off; p < off + dof; ++p) {
5468       const PetscInt gv = cells[p];
5469       PetscInt       lv;
5470 
5471       /* Positions within verticesAdj form 0-based local vertex numbering;
5472          we need to shift it by numCells to get correct DAG points (cells go first) */
5473       PetscCall(PetscFindInt(gv, numVerticesAdj, verticesAdj, &lv));
5474       PetscCheck(lv >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Could not find global vertex %" PetscInt_FMT " in local connectivity", gv);
5475       cones[p] = lv + numCells;
5476     }
5477   }
5478   /* Build point sf */
5479   PetscCall(PetscLayoutCreate(PetscObjectComm((PetscObject)dm), &layout));
5480   PetscCall(PetscLayoutSetSize(layout, NVertices));
5481   PetscCall(PetscLayoutSetLocalSize(layout, numVertices));
5482   PetscCall(PetscLayoutSetBlockSize(layout, 1));
5483   PetscCall(PetscSFCreateByMatchingIndices(layout, numVerticesAdj, verticesAdj, NULL, numCells, numVerticesAdj, verticesAdj, NULL, numCells, vertexSF, &sfPoint));
5484   PetscCall(PetscLayoutDestroy(&layout));
5485   if (!verticesAdjSaved) PetscCall(PetscFree(verticesAdj));
5486   PetscCall(PetscObjectSetName((PetscObject)sfPoint, "point SF"));
5487   if (dm->sf) {
5488     const char *prefix;
5489 
5490     PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm->sf, &prefix));
5491     PetscCall(PetscObjectSetOptionsPrefix((PetscObject)sfPoint, prefix));
5492   }
5493   PetscCall(DMSetPointSF(dm, sfPoint));
5494   PetscCall(PetscSFDestroy(&sfPoint));
5495   if (vertexSF) PetscCall(PetscObjectSetName((PetscObject)*vertexSF, "Vertex Ownership SF"));
5496   /* Fill in the rest of the topology structure */
5497   PetscCall(DMPlexSymmetrize(dm));
5498   PetscCall(DMPlexStratify(dm));
5499   PetscCall(PetscLogEventEnd(DMPLEX_BuildFromCellList, dm, 0, 0, 0));
5500   PetscFunctionReturn(PETSC_SUCCESS);
5501 }
5502 
5503 /*@
5504   DMPlexBuildCoordinatesFromCellListParallel - Build `DM` coordinates from a list of coordinates for each owned vertex (common mesh generator output)
5505 
5506   Collective; No Fortran Support
5507 
5508   Input Parameters:
5509 + dm           - The `DM`
5510 . spaceDim     - The spatial dimension used for coordinates
5511 . sfVert       - `PetscSF` describing complete vertex ownership
5512 - vertexCoords - An array of numVertices*spaceDim numbers, the coordinates of each vertex
5513 
5514   Level: advanced
5515 
5516 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexBuildCoordinatesFromCellList()`, `DMPlexCreateFromCellListParallelPetsc()`, `DMPlexBuildFromCellListParallel()`
5517 @*/
5518 PetscErrorCode DMPlexBuildCoordinatesFromCellListParallel(DM dm, PetscInt spaceDim, PetscSF sfVert, const PetscReal vertexCoords[])
5519 {
5520   PetscSection coordSection;
5521   Vec          coordinates;
5522   PetscScalar *coords;
5523   PetscInt     numVertices, numVerticesAdj, coordSize, v, vStart, vEnd;
5524 
5525   PetscFunctionBegin;
5526   PetscCall(PetscLogEventBegin(DMPLEX_BuildCoordinatesFromCellList, dm, 0, 0, 0));
5527   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
5528   PetscCheck(vStart >= 0 && vEnd >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "DM is not set up properly. DMPlexBuildFromCellList() should be called first.");
5529   PetscCall(DMSetCoordinateDim(dm, spaceDim));
5530   PetscCall(PetscSFGetGraph(sfVert, &numVertices, &numVerticesAdj, NULL, NULL));
5531   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);
5532   PetscCall(DMGetCoordinateSection(dm, &coordSection));
5533   PetscCall(PetscSectionSetNumFields(coordSection, 1));
5534   PetscCall(PetscSectionSetFieldComponents(coordSection, 0, spaceDim));
5535   PetscCall(PetscSectionSetChart(coordSection, vStart, vEnd));
5536   for (v = vStart; v < vEnd; ++v) {
5537     PetscCall(PetscSectionSetDof(coordSection, v, spaceDim));
5538     PetscCall(PetscSectionSetFieldDof(coordSection, v, 0, spaceDim));
5539   }
5540   PetscCall(PetscSectionSetUp(coordSection));
5541   PetscCall(PetscSectionGetStorageSize(coordSection, &coordSize));
5542   PetscCall(VecCreate(PetscObjectComm((PetscObject)dm), &coordinates));
5543   PetscCall(VecSetBlockSize(coordinates, spaceDim));
5544   PetscCall(PetscObjectSetName((PetscObject)coordinates, "coordinates"));
5545   PetscCall(VecSetSizes(coordinates, coordSize, PETSC_DETERMINE));
5546   PetscCall(VecSetType(coordinates, VECSTANDARD));
5547   PetscCall(VecGetArray(coordinates, &coords));
5548   {
5549     MPI_Datatype coordtype;
5550 
5551     /* Need a temp buffer for coords if we have complex/single */
5552     PetscCallMPI(MPI_Type_contiguous((PetscMPIInt)spaceDim, MPIU_SCALAR, &coordtype));
5553     PetscCallMPI(MPI_Type_commit(&coordtype));
5554 #if defined(PETSC_USE_COMPLEX)
5555     {
5556       PetscScalar *svertexCoords;
5557       PetscInt     i;
5558       PetscCall(PetscMalloc1(numVertices * spaceDim, &svertexCoords));
5559       for (i = 0; i < numVertices * spaceDim; i++) svertexCoords[i] = vertexCoords[i];
5560       PetscCall(PetscSFBcastBegin(sfVert, coordtype, svertexCoords, coords, MPI_REPLACE));
5561       PetscCall(PetscSFBcastEnd(sfVert, coordtype, svertexCoords, coords, MPI_REPLACE));
5562       PetscCall(PetscFree(svertexCoords));
5563     }
5564 #else
5565     PetscCall(PetscSFBcastBegin(sfVert, coordtype, vertexCoords, coords, MPI_REPLACE));
5566     PetscCall(PetscSFBcastEnd(sfVert, coordtype, vertexCoords, coords, MPI_REPLACE));
5567 #endif
5568     PetscCallMPI(MPI_Type_free(&coordtype));
5569   }
5570   PetscCall(VecRestoreArray(coordinates, &coords));
5571   PetscCall(DMSetCoordinatesLocal(dm, coordinates));
5572   PetscCall(VecDestroy(&coordinates));
5573   PetscCall(PetscLogEventEnd(DMPLEX_BuildCoordinatesFromCellList, dm, 0, 0, 0));
5574   PetscFunctionReturn(PETSC_SUCCESS);
5575 }
5576 
5577 /*@
5578   DMPlexCreateFromCellListParallelPetsc - Create distributed `DMPLEX` from a list of vertices for each cell (common mesh generator output) where all cells have the same celltype
5579 
5580   Collective
5581 
5582   Input Parameters:
5583 + comm         - The communicator
5584 . dim          - The topological dimension of the mesh
5585 . numCells     - The number of cells owned by this process
5586 . numVertices  - The number of vertices owned by this process, or `PETSC_DECIDE`
5587 . NVertices    - The global number of vertices, or `PETSC_DECIDE`
5588 . numCorners   - The number of vertices for each cell
5589 . interpolate  - Flag indicating that intermediate mesh entities (faces, edges) should be created automatically
5590 . cells        - An array of numCells*numCorners numbers, the global vertex numbers for each cell
5591 . spaceDim     - The spatial dimension used for coordinates
5592 - vertexCoords - An array of numVertices*spaceDim numbers, the coordinates of each vertex
5593 
5594   Output Parameters:
5595 + dm          - The `DM`
5596 . vertexSF    - (Optional) `PetscSF` describing complete vertex ownership
5597 - verticesAdj - (Optional) vertex adjacency array
5598 
5599   Level: intermediate
5600 
5601   Notes:
5602   This function is just a convenient sequence of `DMCreate()`, `DMSetType()`, `DMSetDimension()`,
5603   `DMPlexBuildFromCellListParallel()`, `DMPlexInterpolate()`, `DMPlexBuildCoordinatesFromCellListParallel()`
5604 
5605   See `DMPlexBuildFromCellListParallel()` for an example and details about the topology-related parameters.
5606 
5607   See `DMPlexBuildCoordinatesFromCellListParallel()` for details about the geometry-related parameters.
5608 
5609 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateFromCellListPetsc()`, `DMPlexBuildFromCellListParallel()`, `DMPlexBuildCoordinatesFromCellListParallel()`, `DMPlexCreateFromDAG()`, `DMPlexCreate()`
5610 @*/
5611 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)
5612 {
5613   PetscSF sfVert;
5614 
5615   PetscFunctionBegin;
5616   PetscCall(DMCreate(comm, dm));
5617   PetscCall(DMSetType(*dm, DMPLEX));
5618   PetscValidLogicalCollectiveInt(*dm, dim, 2);
5619   PetscValidLogicalCollectiveInt(*dm, spaceDim, 9);
5620   PetscCall(DMSetDimension(*dm, dim));
5621   PetscCall(DMPlexBuildFromCellListParallel(*dm, numCells, numVertices, NVertices, numCorners, cells, &sfVert, verticesAdj));
5622   if (interpolate) {
5623     DM idm;
5624 
5625     PetscCall(DMPlexInterpolate(*dm, &idm));
5626     PetscCall(DMDestroy(dm));
5627     *dm = idm;
5628   }
5629   PetscCall(DMPlexBuildCoordinatesFromCellListParallel(*dm, spaceDim, sfVert, vertexCoords));
5630   if (vertexSF) *vertexSF = sfVert;
5631   else PetscCall(PetscSFDestroy(&sfVert));
5632   PetscFunctionReturn(PETSC_SUCCESS);
5633 }
5634 
5635 /*@
5636   DMPlexCreateFromCellSectionParallel - Create distributed `DMPLEX` from a list of vertices for each cell (common mesh generator output) and supports multiple celltypes
5637 
5638   Collective
5639 
5640   Input Parameters:
5641 + comm         - The communicator
5642 . dim          - The topological dimension of the mesh
5643 . numCells     - The number of cells owned by this process
5644 . numVertices  - The number of vertices owned by this process, or `PETSC_DECIDE`
5645 . NVertices    - The global number of vertices, or `PETSC_DECIDE`
5646 . cellSection  - The `PetscSection` giving the number of vertices for each cell (layout of cells)
5647 . interpolate  - Flag indicating that intermediate mesh entities (faces, edges) should be created automatically
5648 . cells        - An array of the global vertex numbers for each cell
5649 . spaceDim     - The spatial dimension used for coordinates
5650 - vertexCoords - An array of numVertices*spaceDim numbers, the coordinates of each vertex
5651 
5652   Output Parameters:
5653 + dm          - The `DM`
5654 . vertexSF    - (Optional) `PetscSF` describing complete vertex ownership
5655 - verticesAdj - (Optional) vertex adjacency array
5656 
5657   Level: intermediate
5658 
5659   Notes:
5660   This function is just a convenient sequence of `DMCreate()`, `DMSetType()`, `DMSetDimension()`,
5661   `DMPlexBuildFromCellSectionParallel()`, `DMPlexInterpolate()`, `DMPlexBuildCoordinatesFromCellListParallel()`
5662 
5663   See `DMPlexBuildFromCellSectionParallel()` for an example and details about the topology-related parameters.
5664 
5665   See `DMPlexBuildCoordinatesFromCellListParallel()` for details about the geometry-related parameters.
5666 
5667 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateFromCellListPetsc()`, `DMPlexBuildFromCellListParallel()`, `DMPlexBuildCoordinatesFromCellListParallel()`, `DMPlexCreateFromDAG()`, `DMPlexCreate()`
5668 @*/
5669 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)
5670 {
5671   PetscSF sfVert;
5672 
5673   PetscFunctionBegin;
5674   PetscCall(DMCreate(comm, dm));
5675   PetscCall(DMSetType(*dm, DMPLEX));
5676   PetscValidLogicalCollectiveInt(*dm, dim, 2);
5677   PetscValidLogicalCollectiveInt(*dm, spaceDim, 9);
5678   PetscCall(DMSetDimension(*dm, dim));
5679   PetscCall(DMPlexBuildFromCellSectionParallel(*dm, numCells, numVertices, NVertices, cellSection, cells, &sfVert, verticesAdj));
5680   if (interpolate) {
5681     DM idm;
5682 
5683     PetscCall(DMPlexInterpolate(*dm, &idm));
5684     PetscCall(DMDestroy(dm));
5685     *dm = idm;
5686   }
5687   PetscCall(DMPlexBuildCoordinatesFromCellListParallel(*dm, spaceDim, sfVert, vertexCoords));
5688   if (vertexSF) *vertexSF = sfVert;
5689   else PetscCall(PetscSFDestroy(&sfVert));
5690   PetscFunctionReturn(PETSC_SUCCESS);
5691 }
5692 
5693 /*@
5694   DMPlexBuildFromCellList - Build `DMPLEX` topology from a list of vertices for each cell (common mesh generator output)
5695 
5696   Collective; No Fortran Support
5697 
5698   Input Parameters:
5699 + dm          - The `DM`
5700 . numCells    - The number of cells owned by this process
5701 . numVertices - The number of vertices owned by this process, or `PETSC_DETERMINE`
5702 . numCorners  - The number of vertices for each cell
5703 - cells       - An array of `numCells` x `numCorners` numbers, the global vertex numbers for each cell
5704 
5705   Level: advanced
5706 
5707   Notes:
5708   Two triangles sharing a face
5709 .vb
5710 
5711         2
5712       / | \
5713      /  |  \
5714     /   |   \
5715    0  0 | 1  3
5716     \   |   /
5717      \  |  /
5718       \ | /
5719         1
5720 .ve
5721   would have input
5722 .vb
5723   numCells = 2, numVertices = 4
5724   cells = [0 1 2  1 3 2]
5725 .ve
5726   which would result in the `DMPLEX`
5727 .vb
5728 
5729         4
5730       / | \
5731      /  |  \
5732     /   |   \
5733    2  0 | 1  5
5734     \   |   /
5735      \  |  /
5736       \ | /
5737         3
5738 .ve
5739 
5740   If numVertices is `PETSC_DETERMINE`, it is computed by PETSc as the maximum vertex index in cells + 1.
5741 
5742 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexBuildFromCellListParallel()`, `DMPlexBuildCoordinatesFromCellList()`, `DMPlexCreateFromCellListPetsc()`
5743 @*/
5744 PetscErrorCode DMPlexBuildFromCellList(DM dm, PetscInt numCells, PetscInt numVertices, PetscInt numCorners, const PetscInt cells[])
5745 {
5746   PetscInt *cones, c, p, dim;
5747 
5748   PetscFunctionBegin;
5749   PetscCall(PetscLogEventBegin(DMPLEX_BuildFromCellList, dm, 0, 0, 0));
5750   PetscCall(DMGetDimension(dm, &dim));
5751   /* Get/check global number of vertices */
5752   {
5753     PetscInt       NVerticesInCells, i;
5754     const PetscInt len = numCells * numCorners;
5755 
5756     /* NVerticesInCells = max(cells) + 1 */
5757     NVerticesInCells = PETSC_INT_MIN;
5758     for (i = 0; i < len; i++)
5759       if (cells[i] > NVerticesInCells) NVerticesInCells = cells[i];
5760     ++NVerticesInCells;
5761 
5762     if (numVertices == PETSC_DECIDE) numVertices = NVerticesInCells;
5763     else
5764       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);
5765   }
5766   PetscCall(DMPlexSetChart(dm, 0, numCells + numVertices));
5767   for (c = 0; c < numCells; ++c) PetscCall(DMPlexSetConeSize(dm, c, numCorners));
5768   PetscCall(DMSetUp(dm));
5769   PetscCall(DMPlexGetCones(dm, &cones));
5770   for (c = 0; c < numCells; ++c) {
5771     for (p = 0; p < numCorners; ++p) cones[c * numCorners + p] = cells[c * numCorners + p] + numCells;
5772   }
5773   PetscCall(DMPlexSymmetrize(dm));
5774   PetscCall(DMPlexStratify(dm));
5775   PetscCall(PetscLogEventEnd(DMPLEX_BuildFromCellList, dm, 0, 0, 0));
5776   PetscFunctionReturn(PETSC_SUCCESS);
5777 }
5778 
5779 /*@
5780   DMPlexBuildCoordinatesFromCellList - Build `DM` coordinates from a list of coordinates for each owned vertex (common mesh generator output)
5781 
5782   Collective
5783 
5784   Input Parameters:
5785 + dm           - The `DM`
5786 . spaceDim     - The spatial dimension used for coordinates
5787 - vertexCoords - An array of numVertices*spaceDim numbers, the coordinates of each vertex
5788 
5789   Level: advanced
5790 
5791 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexBuildCoordinatesFromCellListParallel()`, `DMPlexCreateFromCellListPetsc()`, `DMPlexBuildFromCellList()`
5792 @*/
5793 PetscErrorCode DMPlexBuildCoordinatesFromCellList(DM dm, PetscInt spaceDim, const PetscReal vertexCoords[])
5794 {
5795   PetscSection coordSection;
5796   Vec          coordinates;
5797   DM           cdm;
5798   PetscScalar *coords;
5799   PetscInt     v, vStart, vEnd, d;
5800 
5801   PetscFunctionBegin;
5802   PetscCall(PetscLogEventBegin(DMPLEX_BuildCoordinatesFromCellList, dm, 0, 0, 0));
5803   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
5804   PetscCheck(vStart >= 0 && vEnd >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "DM is not set up properly. DMPlexBuildFromCellList() should be called first.");
5805   PetscCall(DMSetCoordinateDim(dm, spaceDim));
5806   PetscCall(DMGetCoordinateSection(dm, &coordSection));
5807   PetscCall(PetscSectionSetNumFields(coordSection, 1));
5808   PetscCall(PetscSectionSetFieldComponents(coordSection, 0, spaceDim));
5809   PetscCall(PetscSectionSetChart(coordSection, vStart, vEnd));
5810   for (v = vStart; v < vEnd; ++v) {
5811     PetscCall(PetscSectionSetDof(coordSection, v, spaceDim));
5812     PetscCall(PetscSectionSetFieldDof(coordSection, v, 0, spaceDim));
5813   }
5814   PetscCall(PetscSectionSetUp(coordSection));
5815 
5816   PetscCall(DMGetCoordinateDM(dm, &cdm));
5817   PetscCall(DMCreateLocalVector(cdm, &coordinates));
5818   PetscCall(VecSetBlockSize(coordinates, spaceDim));
5819   PetscCall(PetscObjectSetName((PetscObject)coordinates, "coordinates"));
5820   PetscCall(VecGetArrayWrite(coordinates, &coords));
5821   for (v = 0; v < vEnd - vStart; ++v) {
5822     for (d = 0; d < spaceDim; ++d) coords[v * spaceDim + d] = vertexCoords[v * spaceDim + d];
5823   }
5824   PetscCall(VecRestoreArrayWrite(coordinates, &coords));
5825   PetscCall(DMSetCoordinatesLocal(dm, coordinates));
5826   PetscCall(VecDestroy(&coordinates));
5827   PetscCall(PetscLogEventEnd(DMPLEX_BuildCoordinatesFromCellList, dm, 0, 0, 0));
5828   PetscFunctionReturn(PETSC_SUCCESS);
5829 }
5830 
5831 /*@
5832   DMPlexCreateFromCellListPetsc - Create `DMPLEX` from a list of vertices for each cell (common mesh generator output), but only process 0 takes in the input
5833 
5834   Collective
5835 
5836   Input Parameters:
5837 + comm         - The communicator
5838 . dim          - The topological dimension of the mesh
5839 . numCells     - The number of cells, only on process 0
5840 . numVertices  - The number of vertices owned by this process, or `PETSC_DECIDE`, only on process 0
5841 . numCorners   - The number of vertices for each cell, only on process 0
5842 . interpolate  - Flag indicating that intermediate mesh entities (faces, edges) should be created automatically
5843 . cells        - An array of numCells*numCorners numbers, the vertices for each cell, only on process 0
5844 . spaceDim     - The spatial dimension used for coordinates
5845 - vertexCoords - An array of numVertices*spaceDim numbers, the coordinates of each vertex, only on process 0
5846 
5847   Output Parameter:
5848 . dm - The `DM`, which only has points on process 0
5849 
5850   Level: intermediate
5851 
5852   Notes:
5853   This function is just a convenient sequence of `DMCreate()`, `DMSetType()`, `DMSetDimension()`, `DMPlexBuildFromCellList()`,
5854   `DMPlexInterpolate()`, `DMPlexBuildCoordinatesFromCellList()`
5855 
5856   See `DMPlexBuildFromCellList()` for an example and details about the topology-related parameters.
5857   See `DMPlexBuildCoordinatesFromCellList()` for details about the geometry-related parameters.
5858   See `DMPlexCreateFromCellListParallelPetsc()` for parallel input
5859 
5860 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateFromCellListParallelPetsc()`, `DMPlexBuildFromCellList()`, `DMPlexBuildCoordinatesFromCellList()`, `DMPlexCreateFromDAG()`, `DMPlexCreate()`
5861 @*/
5862 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)
5863 {
5864   PetscMPIInt rank;
5865 
5866   PetscFunctionBegin;
5867   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.");
5868   PetscCallMPI(MPI_Comm_rank(comm, &rank));
5869   PetscCall(DMCreate(comm, dm));
5870   PetscCall(DMSetType(*dm, DMPLEX));
5871   PetscCall(DMSetDimension(*dm, dim));
5872   if (rank == 0) PetscCall(DMPlexBuildFromCellList(*dm, numCells, numVertices, numCorners, cells));
5873   else PetscCall(DMPlexBuildFromCellList(*dm, 0, 0, 0, NULL));
5874   if (interpolate) {
5875     DM idm;
5876 
5877     PetscCall(DMPlexInterpolate(*dm, &idm));
5878     PetscCall(DMDestroy(dm));
5879     *dm = idm;
5880   }
5881   if (rank == 0) PetscCall(DMPlexBuildCoordinatesFromCellList(*dm, spaceDim, vertexCoords));
5882   else PetscCall(DMPlexBuildCoordinatesFromCellList(*dm, spaceDim, NULL));
5883   PetscFunctionReturn(PETSC_SUCCESS);
5884 }
5885 
5886 /*@
5887   DMPlexCreateFromDAG - This takes as input the adjacency-list representation of the Directed Acyclic Graph (Hasse Diagram) encoding a mesh, and produces a `DM`
5888 
5889   Input Parameters:
5890 + dm               - The empty `DM` object, usually from `DMCreate()` and `DMSetDimension()`
5891 . depth            - The depth of the DAG
5892 . numPoints        - Array of size depth + 1 containing the number of points at each `depth`
5893 . coneSize         - The cone size of each point
5894 . cones            - The concatenation of the cone points for each point, the cone list must be oriented correctly for each point
5895 . coneOrientations - The orientation of each cone point
5896 - vertexCoords     - An array of `numPoints`[0]*spacedim numbers representing the coordinates of each vertex, with spacedim the value set via `DMSetCoordinateDim()`
5897 
5898   Output Parameter:
5899 . dm - The `DM`
5900 
5901   Level: advanced
5902 
5903   Note:
5904   Two triangles sharing a face would have input
5905 .vb
5906   depth = 1, numPoints = [4 2], coneSize = [3 3 0 0 0 0]
5907   cones = [2 3 4  3 5 4], coneOrientations = [0 0 0  0 0 0]
5908  vertexCoords = [-1.0 0.0  0.0 -1.0  0.0 1.0  1.0 0.0]
5909 .ve
5910   which would result in the DMPlex
5911 .vb
5912         4
5913       / | \
5914      /  |  \
5915     /   |   \
5916    2  0 | 1  5
5917     \   |   /
5918      \  |  /
5919       \ | /
5920         3
5921 .ve
5922   Notice that all points are numbered consecutively, unlike `DMPlexCreateFromCellListPetsc()`
5923 
5924 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateFromCellListPetsc()`, `DMPlexCreate()`
5925 @*/
5926 PetscErrorCode DMPlexCreateFromDAG(DM dm, PetscInt depth, const PetscInt numPoints[], const PetscInt coneSize[], const PetscInt cones[], const PetscInt coneOrientations[], const PetscScalar vertexCoords[])
5927 {
5928   Vec          coordinates;
5929   PetscSection coordSection;
5930   PetscScalar *coords;
5931   PetscInt     coordSize, firstVertex = -1, pStart = 0, pEnd = 0, p, v, dim, dimEmbed, d, off;
5932 
5933   PetscFunctionBegin;
5934   PetscCall(DMGetDimension(dm, &dim));
5935   PetscCall(DMGetCoordinateDim(dm, &dimEmbed));
5936   PetscCheck(dimEmbed >= dim, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Embedding dimension %" PetscInt_FMT " cannot be less than intrinsic dimension %" PetscInt_FMT, dimEmbed, dim);
5937   for (d = 0; d <= depth; ++d) pEnd += numPoints[d];
5938   PetscCall(DMPlexSetChart(dm, pStart, pEnd));
5939   for (p = pStart; p < pEnd; ++p) {
5940     PetscCall(DMPlexSetConeSize(dm, p, coneSize[p - pStart]));
5941     if (firstVertex < 0 && !coneSize[p - pStart]) firstVertex = p - pStart;
5942   }
5943   PetscCheck(firstVertex >= 0 || !numPoints[0], PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Expected %" PetscInt_FMT " vertices but could not find any", numPoints[0]);
5944   PetscCall(DMSetUp(dm)); /* Allocate space for cones */
5945   for (p = pStart, off = 0; p < pEnd; off += coneSize[p - pStart], ++p) {
5946     PetscCall(DMPlexSetCone(dm, p, &cones[off]));
5947     PetscCall(DMPlexSetConeOrientation(dm, p, &coneOrientations[off]));
5948   }
5949   PetscCall(DMPlexSymmetrize(dm));
5950   PetscCall(DMPlexStratify(dm));
5951   /* Build coordinates */
5952   PetscCall(DMGetCoordinateSection(dm, &coordSection));
5953   PetscCall(PetscSectionSetNumFields(coordSection, 1));
5954   PetscCall(PetscSectionSetFieldComponents(coordSection, 0, dimEmbed));
5955   PetscCall(PetscSectionSetChart(coordSection, firstVertex, firstVertex + numPoints[0]));
5956   for (v = firstVertex; v < firstVertex + numPoints[0]; ++v) {
5957     PetscCall(PetscSectionSetDof(coordSection, v, dimEmbed));
5958     PetscCall(PetscSectionSetFieldDof(coordSection, v, 0, dimEmbed));
5959   }
5960   PetscCall(PetscSectionSetUp(coordSection));
5961   PetscCall(PetscSectionGetStorageSize(coordSection, &coordSize));
5962   PetscCall(VecCreate(PETSC_COMM_SELF, &coordinates));
5963   PetscCall(PetscObjectSetName((PetscObject)coordinates, "coordinates"));
5964   PetscCall(VecSetSizes(coordinates, coordSize, PETSC_DETERMINE));
5965   PetscCall(VecSetBlockSize(coordinates, dimEmbed));
5966   PetscCall(VecSetType(coordinates, VECSTANDARD));
5967   if (vertexCoords) {
5968     PetscCall(VecGetArray(coordinates, &coords));
5969     for (v = 0; v < numPoints[0]; ++v) {
5970       PetscInt off;
5971 
5972       PetscCall(PetscSectionGetOffset(coordSection, v + firstVertex, &off));
5973       for (d = 0; d < dimEmbed; ++d) coords[off + d] = vertexCoords[v * dimEmbed + d];
5974     }
5975   }
5976   PetscCall(VecRestoreArray(coordinates, &coords));
5977   PetscCall(DMSetCoordinatesLocal(dm, coordinates));
5978   PetscCall(VecDestroy(&coordinates));
5979   PetscFunctionReturn(PETSC_SUCCESS);
5980 }
5981 
5982 /*
5983   DMPlexCreateCellVertexFromFile - Create a `DMPLEX` mesh from a simple cell-vertex file.
5984 
5985   Collective
5986 
5987 + comm        - The MPI communicator
5988 . filename    - Name of the .dat file
5989 - interpolate - Create faces and edges in the mesh
5990 
5991   Output Parameter:
5992 . dm  - The `DM` object representing the mesh
5993 
5994   Level: beginner
5995 
5996   Note:
5997   The format is the simplest possible:
5998 .vb
5999   dim Ne Nv Nc Nl
6000   v_1 v_2 ... v_Nc
6001   ...
6002   x y z marker_1 ... marker_Nl
6003 .ve
6004 
6005   Developer Note:
6006   Should use a `PetscViewer` not a filename
6007 
6008 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateFromFile()`, `DMPlexCreateGmsh()`, `DMPlexCreate()`
6009 */
6010 static PetscErrorCode DMPlexCreateCellVertexFromFile(MPI_Comm comm, const char filename[], PetscBool interpolate, DM *dm)
6011 {
6012   DMLabel      marker;
6013   PetscViewer  viewer;
6014   Vec          coordinates;
6015   PetscSection coordSection;
6016   PetscScalar *coords;
6017   char         line[PETSC_MAX_PATH_LEN];
6018   PetscInt     cdim, coordSize, v, c, d;
6019   PetscMPIInt  rank;
6020   int          snum, dim, Nv, Nc, Ncn, Nl;
6021 
6022   PetscFunctionBegin;
6023   PetscCallMPI(MPI_Comm_rank(comm, &rank));
6024   PetscCall(PetscViewerCreate(comm, &viewer));
6025   PetscCall(PetscViewerSetType(viewer, PETSCVIEWERASCII));
6026   PetscCall(PetscViewerFileSetMode(viewer, FILE_MODE_READ));
6027   PetscCall(PetscViewerFileSetName(viewer, filename));
6028   if (rank == 0) {
6029     PetscCall(PetscViewerRead(viewer, line, 5, NULL, PETSC_STRING));
6030     snum = sscanf(line, "%d %d %d %d %d", &dim, &Nc, &Nv, &Ncn, &Nl);
6031     PetscCheck(snum == 5, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unable to parse cell-vertex file: %s", line);
6032   } else {
6033     Nc = Nv = Ncn = Nl = 0;
6034   }
6035   PetscCallMPI(MPI_Bcast(&dim, 1, MPI_INT, 0, comm));
6036   cdim = (PetscInt)dim;
6037   PetscCall(DMCreate(comm, dm));
6038   PetscCall(DMSetType(*dm, DMPLEX));
6039   PetscCall(DMPlexSetChart(*dm, 0, Nc + Nv));
6040   PetscCall(DMSetDimension(*dm, (PetscInt)dim));
6041   PetscCall(DMSetCoordinateDim(*dm, cdim));
6042   /* Read topology */
6043   if (rank == 0) {
6044     char     format[PETSC_MAX_PATH_LEN];
6045     PetscInt cone[8];
6046     int      vbuf[8], v;
6047 
6048     for (c = 0; c < Ncn; ++c) {
6049       format[c * 3 + 0] = '%';
6050       format[c * 3 + 1] = 'd';
6051       format[c * 3 + 2] = ' ';
6052     }
6053     format[Ncn * 3 - 1] = '\0';
6054     for (c = 0; c < Nc; ++c) PetscCall(DMPlexSetConeSize(*dm, c, Ncn));
6055     PetscCall(DMSetUp(*dm));
6056     for (c = 0; c < Nc; ++c) {
6057       PetscCall(PetscViewerRead(viewer, line, Ncn, NULL, PETSC_STRING));
6058       switch (Ncn) {
6059       case 2:
6060         snum = sscanf(line, format, &vbuf[0], &vbuf[1]);
6061         break;
6062       case 3:
6063         snum = sscanf(line, format, &vbuf[0], &vbuf[1], &vbuf[2]);
6064         break;
6065       case 4:
6066         snum = sscanf(line, format, &vbuf[0], &vbuf[1], &vbuf[2], &vbuf[3]);
6067         break;
6068       case 6:
6069         snum = sscanf(line, format, &vbuf[0], &vbuf[1], &vbuf[2], &vbuf[3], &vbuf[4], &vbuf[5]);
6070         break;
6071       case 8:
6072         snum = sscanf(line, format, &vbuf[0], &vbuf[1], &vbuf[2], &vbuf[3], &vbuf[4], &vbuf[5], &vbuf[6], &vbuf[7]);
6073         break;
6074       default:
6075         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "No cell shape with %d vertices", Ncn);
6076       }
6077       PetscCheck(snum == Ncn, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unable to parse cell-vertex file: %s", line);
6078       for (v = 0; v < Ncn; ++v) cone[v] = vbuf[v] + Nc;
6079       /* Hexahedra are inverted */
6080       if (Ncn == 8) {
6081         PetscInt tmp = cone[1];
6082         cone[1]      = cone[3];
6083         cone[3]      = tmp;
6084       }
6085       PetscCall(DMPlexSetCone(*dm, c, cone));
6086     }
6087   }
6088   PetscCall(DMPlexSymmetrize(*dm));
6089   PetscCall(DMPlexStratify(*dm));
6090   /* Read coordinates */
6091   PetscCall(DMGetCoordinateSection(*dm, &coordSection));
6092   PetscCall(PetscSectionSetNumFields(coordSection, 1));
6093   PetscCall(PetscSectionSetFieldComponents(coordSection, 0, cdim));
6094   PetscCall(PetscSectionSetChart(coordSection, Nc, Nc + Nv));
6095   for (v = Nc; v < Nc + Nv; ++v) {
6096     PetscCall(PetscSectionSetDof(coordSection, v, cdim));
6097     PetscCall(PetscSectionSetFieldDof(coordSection, v, 0, cdim));
6098   }
6099   PetscCall(PetscSectionSetUp(coordSection));
6100   PetscCall(PetscSectionGetStorageSize(coordSection, &coordSize));
6101   PetscCall(VecCreate(PETSC_COMM_SELF, &coordinates));
6102   PetscCall(PetscObjectSetName((PetscObject)coordinates, "coordinates"));
6103   PetscCall(VecSetSizes(coordinates, coordSize, PETSC_DETERMINE));
6104   PetscCall(VecSetBlockSize(coordinates, cdim));
6105   PetscCall(VecSetType(coordinates, VECSTANDARD));
6106   PetscCall(VecGetArray(coordinates, &coords));
6107   if (rank == 0) {
6108     char   format[PETSC_MAX_PATH_LEN];
6109     double x[3];
6110     int    l, val[3];
6111 
6112     if (Nl) {
6113       for (l = 0; l < Nl; ++l) {
6114         format[l * 3 + 0] = '%';
6115         format[l * 3 + 1] = 'd';
6116         format[l * 3 + 2] = ' ';
6117       }
6118       format[Nl * 3 - 1] = '\0';
6119       PetscCall(DMCreateLabel(*dm, "marker"));
6120       PetscCall(DMGetLabel(*dm, "marker", &marker));
6121     }
6122     for (v = 0; v < Nv; ++v) {
6123       PetscCall(PetscViewerRead(viewer, line, 3 + Nl, NULL, PETSC_STRING));
6124       snum = sscanf(line, "%lg %lg %lg", &x[0], &x[1], &x[2]);
6125       PetscCheck(snum == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unable to parse cell-vertex file: %s", line);
6126       switch (Nl) {
6127       case 0:
6128         snum = 0;
6129         break;
6130       case 1:
6131         snum = sscanf(line, format, &val[0]);
6132         break;
6133       case 2:
6134         snum = sscanf(line, format, &val[0], &val[1]);
6135         break;
6136       case 3:
6137         snum = sscanf(line, format, &val[0], &val[1], &val[2]);
6138         break;
6139       default:
6140         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Request support for %d labels", Nl);
6141       }
6142       PetscCheck(snum == Nl, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unable to parse cell-vertex file: %s", line);
6143       for (d = 0; d < cdim; ++d) coords[v * cdim + d] = x[d];
6144       for (l = 0; l < Nl; ++l) PetscCall(DMLabelSetValue(marker, v + Nc, val[l]));
6145     }
6146   }
6147   PetscCall(VecRestoreArray(coordinates, &coords));
6148   PetscCall(DMSetCoordinatesLocal(*dm, coordinates));
6149   PetscCall(VecDestroy(&coordinates));
6150   PetscCall(PetscViewerDestroy(&viewer));
6151   if (interpolate) {
6152     DM      idm;
6153     DMLabel bdlabel;
6154 
6155     PetscCall(DMPlexInterpolate(*dm, &idm));
6156     PetscCall(DMDestroy(dm));
6157     *dm = idm;
6158 
6159     if (!Nl) {
6160       PetscCall(DMCreateLabel(*dm, "marker"));
6161       PetscCall(DMGetLabel(*dm, "marker", &bdlabel));
6162       PetscCall(DMPlexMarkBoundaryFaces(*dm, PETSC_DETERMINE, bdlabel));
6163       PetscCall(DMPlexLabelComplete(*dm, bdlabel));
6164     }
6165   }
6166   PetscFunctionReturn(PETSC_SUCCESS);
6167 }
6168 
6169 /*@
6170   DMPlexCreateFromFile - This takes a filename and produces a `DM`
6171 
6172   Collective
6173 
6174   Input Parameters:
6175 + comm        - The communicator
6176 . filename    - A file name
6177 . plexname    - The object name of the resulting `DM`, also used for intra-datafile lookup by some formats
6178 - interpolate - Flag to create intermediate mesh pieces (edges, faces)
6179 
6180   Output Parameter:
6181 . dm - The `DM`
6182 
6183   Options Database Key:
6184 . -dm_plex_create_from_hdf5_xdmf - use the `PETSC_VIEWER_HDF5_XDMF` format for reading HDF5
6185 
6186   Use `-dm_plex_create_ prefix` to pass options to the internal `PetscViewer`, e.g.
6187 $ -dm_plex_create_viewer_hdf5_collective
6188 
6189   Level: beginner
6190 
6191   Notes:
6192   Using `PETSCVIEWERHDF5` type with `PETSC_VIEWER_HDF5_PETSC` format, one can save multiple `DMPLEX`
6193   meshes in a single HDF5 file. This in turn requires one to name the `DMPLEX` object with `PetscObjectSetName()`
6194   before saving it with `DMView()` and before loading it with `DMLoad()` for identification of the mesh object.
6195   The input parameter name is thus used to name the `DMPLEX` object when `DMPlexCreateFromFile()` internally
6196   calls `DMLoad()`. Currently, name is ignored for other viewer types and/or formats.
6197 
6198 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateFromDAG()`, `DMPlexCreateFromCellListPetsc()`, `DMPlexCreate()`, `PetscObjectSetName()`, `DMView()`, `DMLoad()`
6199 @*/
6200 PetscErrorCode DMPlexCreateFromFile(MPI_Comm comm, const char filename[], const char plexname[], PetscBool interpolate, DM *dm)
6201 {
6202   const char  extGmsh[]      = ".msh";
6203   const char  extGmsh2[]     = ".msh2";
6204   const char  extGmsh4[]     = ".msh4";
6205   const char  extCGNS[]      = ".cgns";
6206   const char  extExodus[]    = ".exo";
6207   const char  extExodus_e[]  = ".e";
6208   const char  extGenesis[]   = ".gen";
6209   const char  extFluent[]    = ".cas";
6210   const char  extHDF5[]      = ".h5";
6211   const char  extXDMFHDF5[]  = ".xdmf.h5";
6212   const char  extPLY[]       = ".ply";
6213   const char  extEGADSLite[] = ".egadslite";
6214   const char  extEGADS[]     = ".egads";
6215   const char  extIGES[]      = ".igs";
6216   const char  extSTEP[]      = ".stp";
6217   const char  extCV[]        = ".dat";
6218   size_t      len;
6219   PetscBool   isGmsh, isGmsh2, isGmsh4, isCGNS, isExodus, isGenesis, isFluent, isHDF5, isPLY, isEGADSLite, isEGADS, isIGES, isSTEP, isCV, isXDMFHDF5;
6220   PetscMPIInt rank;
6221 
6222   PetscFunctionBegin;
6223   PetscAssertPointer(filename, 2);
6224   if (plexname) PetscAssertPointer(plexname, 3);
6225   PetscAssertPointer(dm, 5);
6226   PetscCall(DMInitializePackage());
6227   PetscCall(PetscLogEventBegin(DMPLEX_CreateFromFile, 0, 0, 0, 0));
6228   PetscCallMPI(MPI_Comm_rank(comm, &rank));
6229   PetscCall(PetscStrlen(filename, &len));
6230   PetscCheck(len, comm, PETSC_ERR_ARG_WRONG, "Filename must be a valid path");
6231 
6232 #define CheckExtension(extension__, is_extension__) \
6233   do { \
6234     PetscAssert(sizeof(extension__), comm, PETSC_ERR_PLIB, "Zero-size extension: %s", extension__); \
6235     /* don't count the null-terminator at the end */ \
6236     const size_t ext_len = sizeof(extension__) - 1; \
6237     if (len < ext_len) { \
6238       is_extension__ = PETSC_FALSE; \
6239     } else { \
6240       PetscCall(PetscStrncmp(filename + len - ext_len, extension__, ext_len, &is_extension__)); \
6241     } \
6242   } while (0)
6243 
6244   CheckExtension(extGmsh, isGmsh);
6245   CheckExtension(extGmsh2, isGmsh2);
6246   CheckExtension(extGmsh4, isGmsh4);
6247   CheckExtension(extCGNS, isCGNS);
6248   CheckExtension(extExodus, isExodus);
6249   if (!isExodus) CheckExtension(extExodus_e, isExodus);
6250   CheckExtension(extGenesis, isGenesis);
6251   CheckExtension(extFluent, isFluent);
6252   CheckExtension(extHDF5, isHDF5);
6253   CheckExtension(extPLY, isPLY);
6254   CheckExtension(extEGADSLite, isEGADSLite);
6255   CheckExtension(extEGADS, isEGADS);
6256   CheckExtension(extIGES, isIGES);
6257   CheckExtension(extSTEP, isSTEP);
6258   CheckExtension(extCV, isCV);
6259   CheckExtension(extXDMFHDF5, isXDMFHDF5);
6260 
6261 #undef CheckExtension
6262 
6263   if (isGmsh || isGmsh2 || isGmsh4) {
6264     PetscCall(DMPlexCreateGmshFromFile(comm, filename, interpolate, dm));
6265   } else if (isCGNS) {
6266     PetscCall(DMPlexCreateCGNSFromFile(comm, filename, interpolate, dm));
6267   } else if (isExodus || isGenesis) {
6268     PetscCall(DMPlexCreateExodusFromFile(comm, filename, interpolate, dm));
6269   } else if (isFluent) {
6270     PetscCall(DMPlexCreateFluentFromFile(comm, filename, interpolate, dm));
6271   } else if (isHDF5) {
6272     PetscViewer viewer;
6273 
6274     /* PETSC_VIEWER_HDF5_XDMF is used if the filename ends with .xdmf.h5, or if -dm_plex_create_from_hdf5_xdmf option is present */
6275     PetscCall(PetscOptionsGetBool(NULL, NULL, "-dm_plex_create_from_hdf5_xdmf", &isXDMFHDF5, NULL));
6276     PetscCall(PetscViewerCreate(comm, &viewer));
6277     PetscCall(PetscViewerSetType(viewer, PETSCVIEWERHDF5));
6278     PetscCall(PetscViewerSetOptionsPrefix(viewer, "dm_plex_create_"));
6279     PetscCall(PetscViewerSetFromOptions(viewer));
6280     PetscCall(PetscViewerFileSetMode(viewer, FILE_MODE_READ));
6281     PetscCall(PetscViewerFileSetName(viewer, filename));
6282 
6283     PetscCall(DMCreate(comm, dm));
6284     PetscCall(PetscObjectSetName((PetscObject)*dm, plexname));
6285     PetscCall(DMSetType(*dm, DMPLEX));
6286     if (isXDMFHDF5) PetscCall(PetscViewerPushFormat(viewer, PETSC_VIEWER_HDF5_XDMF));
6287     PetscCall(DMLoad(*dm, viewer));
6288     if (isXDMFHDF5) PetscCall(PetscViewerPopFormat(viewer));
6289     PetscCall(PetscViewerDestroy(&viewer));
6290 
6291     if (interpolate) {
6292       DM idm;
6293 
6294       PetscCall(DMPlexInterpolate(*dm, &idm));
6295       PetscCall(DMDestroy(dm));
6296       *dm = idm;
6297     }
6298   } else if (isPLY) {
6299     PetscCall(DMPlexCreatePLYFromFile(comm, filename, interpolate, dm));
6300   } else if (isEGADSLite || isEGADS || isIGES || isSTEP) {
6301     if (isEGADSLite) PetscCall(DMPlexCreateEGADSLiteFromFile(comm, filename, dm));
6302     else PetscCall(DMPlexCreateEGADSFromFile(comm, filename, dm));
6303     if (!interpolate) {
6304       DM udm;
6305 
6306       PetscCall(DMPlexUninterpolate(*dm, &udm));
6307       PetscCall(DMDestroy(dm));
6308       *dm = udm;
6309     }
6310   } else if (isCV) {
6311     PetscCall(DMPlexCreateCellVertexFromFile(comm, filename, interpolate, dm));
6312   } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cannot load file %s: unrecognized extension", filename);
6313   PetscCall(PetscStrlen(plexname, &len));
6314   if (len) PetscCall(PetscObjectSetName((PetscObject)*dm, plexname));
6315   PetscCall(PetscLogEventEnd(DMPLEX_CreateFromFile, 0, 0, 0, 0));
6316   PetscFunctionReturn(PETSC_SUCCESS);
6317 }
6318 
6319 /*@
6320   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.
6321 
6322   Input Parameters:
6323 + tr     - The `DMPlexTransform`
6324 - prefix - An options prefix, or NULL
6325 
6326   Output Parameter:
6327 . dm - The `DM`
6328 
6329   Level: beginner
6330 
6331   Notes:
6332   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.
6333 
6334 .seealso: `DMPlexCreateFromFile`, `DMPlexCreateFromDAG()`, `DMPlexCreateFromCellListPetsc()`, `DMPlexCreate()`
6335 @*/
6336 PetscErrorCode DMPlexCreateEphemeral(DMPlexTransform tr, const char prefix[], DM *dm)
6337 {
6338   DM           bdm, bcdm, cdm;
6339   Vec          coordinates, coordinatesNew;
6340   PetscSection cs;
6341   PetscInt     cdim, Nl;
6342 
6343   PetscFunctionBegin;
6344   PetscCall(DMCreate(PetscObjectComm((PetscObject)tr), dm));
6345   PetscCall(DMSetType(*dm, DMPLEX));
6346   ((DM_Plex *)(*dm)->data)->interpolated = DMPLEX_INTERPOLATED_FULL;
6347   // Handle coordinates
6348   PetscCall(DMPlexTransformGetDM(tr, &bdm));
6349   PetscCall(DMPlexTransformSetDimensions(tr, bdm, *dm));
6350   PetscCall(DMGetCoordinateDim(*dm, &cdim));
6351   PetscCall(DMGetCoordinateDM(bdm, &bcdm));
6352   PetscCall(DMGetCoordinateDM(*dm, &cdm));
6353   PetscCall(DMCopyDisc(bcdm, cdm));
6354   PetscCall(DMGetLocalSection(cdm, &cs));
6355   PetscCall(PetscSectionSetNumFields(cs, 1));
6356   PetscCall(PetscSectionSetFieldComponents(cs, 0, cdim));
6357   PetscCall(DMGetCoordinatesLocal(bdm, &coordinates));
6358   PetscCall(VecDuplicate(coordinates, &coordinatesNew));
6359   PetscCall(VecCopy(coordinates, coordinatesNew));
6360   PetscCall(DMSetCoordinatesLocal(*dm, coordinatesNew));
6361   PetscCall(VecDestroy(&coordinatesNew));
6362 
6363   PetscCall(PetscObjectReference((PetscObject)tr));
6364   PetscCall(DMPlexTransformDestroy(&((DM_Plex *)(*dm)->data)->tr));
6365   ((DM_Plex *)(*dm)->data)->tr = tr;
6366   PetscCall(DMPlexDistributeSetDefault(*dm, PETSC_FALSE));
6367   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*dm, prefix));
6368   PetscCall(DMSetFromOptions(*dm));
6369 
6370   PetscCall(DMGetNumLabels(bdm, &Nl));
6371   for (PetscInt l = 0; l < Nl; ++l) {
6372     DMLabel     label, labelNew;
6373     const char *lname;
6374     PetscBool   isDepth, isCellType;
6375 
6376     PetscCall(DMGetLabelName(bdm, l, &lname));
6377     PetscCall(PetscStrcmp(lname, "depth", &isDepth));
6378     if (isDepth) continue;
6379     PetscCall(PetscStrcmp(lname, "celltype", &isCellType));
6380     if (isCellType) continue;
6381     PetscCall(DMCreateLabel(*dm, lname));
6382     PetscCall(DMGetLabel(bdm, lname, &label));
6383     PetscCall(DMGetLabel(*dm, lname, &labelNew));
6384     PetscCall(DMLabelSetType(labelNew, DMLABELEPHEMERAL));
6385     PetscCall(DMLabelEphemeralSetLabel(labelNew, label));
6386     PetscCall(DMLabelEphemeralSetTransform(labelNew, tr));
6387     PetscCall(DMLabelSetUp(labelNew));
6388   }
6389   PetscFunctionReturn(PETSC_SUCCESS);
6390 }
6391