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