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