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