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