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