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