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