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