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