xref: /petsc/src/dm/impls/plex/plexcreate.c (revision 9f6adcc9204346fb67e1a483d37adf232b1bdea0)
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 
5148   PetscCall(DMInitialize_Plex(dm));
5149   PetscFunctionReturn(PETSC_SUCCESS);
5150 }
5151 
5152 /*@
5153   DMPlexCreate - Creates a `DMPLEX` object, which encapsulates an unstructured mesh, or CW complex, which can be expressed using a Hasse Diagram.
5154 
5155   Collective
5156 
5157   Input Parameter:
5158 . comm - The communicator for the `DMPLEX` object
5159 
5160   Output Parameter:
5161 . mesh - The `DMPLEX` object
5162 
5163   Level: beginner
5164 
5165 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMType`, `DMCreate()`, `DMSetType()`
5166 @*/
5167 PetscErrorCode DMPlexCreate(MPI_Comm comm, DM *mesh)
5168 {
5169   PetscFunctionBegin;
5170   PetscAssertPointer(mesh, 2);
5171   PetscCall(DMCreate(comm, mesh));
5172   PetscCall(DMSetType(*mesh, DMPLEX));
5173   PetscFunctionReturn(PETSC_SUCCESS);
5174 }
5175 
5176 /*@C
5177   DMPlexBuildFromCellListParallel - Build distributed `DMPLEX` topology from a list of vertices for each cell (common mesh generator output)
5178 
5179   Collective; No Fortran Support
5180 
5181   Input Parameters:
5182 + dm          - The `DM`
5183 . numCells    - The number of cells owned by this process
5184 . numVertices - The number of vertices to be owned by this process, or `PETSC_DECIDE`
5185 . NVertices   - The global number of vertices, or `PETSC_DETERMINE`
5186 . numCorners  - The number of vertices for each cell
5187 - cells       - An array of numCells*numCorners numbers, the global vertex numbers for each cell
5188 
5189   Output Parameters:
5190 + vertexSF         - (Optional) `PetscSF` describing complete vertex ownership
5191 - verticesAdjSaved - (Optional) vertex adjacency array
5192 
5193   Level: advanced
5194 
5195   Notes:
5196   Two triangles sharing a face
5197 .vb
5198 
5199         2
5200       / | \
5201      /  |  \
5202     /   |   \
5203    0  0 | 1  3
5204     \   |   /
5205      \  |  /
5206       \ | /
5207         1
5208 .ve
5209   would have input
5210 .vb
5211   numCells = 2, numVertices = 4
5212   cells = [0 1 2  1 3 2]
5213 .ve
5214   which would result in the `DMPLEX`
5215 .vb
5216 
5217         4
5218       / | \
5219      /  |  \
5220     /   |   \
5221    2  0 | 1  5
5222     \   |   /
5223      \  |  /
5224       \ | /
5225         3
5226 .ve
5227 
5228   Vertices are implicitly numbered consecutively 0,...,NVertices.
5229   Each rank owns a chunk of numVertices consecutive vertices.
5230   If numVertices is `PETSC_DECIDE`, PETSc will distribute them as evenly as possible using PetscLayout.
5231   If NVertices is `PETSC_DETERMINE` and numVertices is PETSC_DECIDE, NVertices is computed by PETSc as the maximum vertex index in cells + 1.
5232   If only NVertices is `PETSC_DETERMINE`, it is computed as the sum of numVertices over all ranks.
5233 
5234   The cell distribution is arbitrary non-overlapping, independent of the vertex distribution.
5235 
5236 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexBuildFromCellList()`, `DMPlexCreateFromCellListParallelPetsc()`, `DMPlexBuildCoordinatesFromCellListParallel()`,
5237           `PetscSF`
5238 @*/
5239 PetscErrorCode DMPlexBuildFromCellListParallel(DM dm, PetscInt numCells, PetscInt numVertices, PetscInt NVertices, PetscInt numCorners, const PetscInt cells[], PetscSF *vertexSF, PetscInt **verticesAdjSaved)
5240 {
5241   PetscSF     sfPoint;
5242   PetscLayout layout;
5243   PetscInt    numVerticesAdj, *verticesAdj, *cones, c, p;
5244 
5245   PetscFunctionBegin;
5246   PetscValidLogicalCollectiveInt(dm, NVertices, 4);
5247   PetscCall(PetscLogEventBegin(DMPLEX_BuildFromCellList, dm, 0, 0, 0));
5248   /* Get/check global number of vertices */
5249   {
5250     PetscInt       NVerticesInCells, i;
5251     const PetscInt len = numCells * numCorners;
5252 
5253     /* NVerticesInCells = max(cells) + 1 */
5254     NVerticesInCells = PETSC_MIN_INT;
5255     for (i = 0; i < len; i++)
5256       if (cells[i] > NVerticesInCells) NVerticesInCells = cells[i];
5257     ++NVerticesInCells;
5258     PetscCall(MPIU_Allreduce(MPI_IN_PLACE, &NVerticesInCells, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
5259 
5260     if (numVertices == PETSC_DECIDE && NVertices == PETSC_DECIDE) NVertices = NVerticesInCells;
5261     else
5262       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);
5263   }
5264   /* Count locally unique vertices */
5265   {
5266     PetscHSetI vhash;
5267     PetscInt   off = 0;
5268 
5269     PetscCall(PetscHSetICreate(&vhash));
5270     for (c = 0; c < numCells; ++c) {
5271       for (p = 0; p < numCorners; ++p) PetscCall(PetscHSetIAdd(vhash, cells[c * numCorners + p]));
5272     }
5273     PetscCall(PetscHSetIGetSize(vhash, &numVerticesAdj));
5274     if (!verticesAdjSaved) PetscCall(PetscMalloc1(numVerticesAdj, &verticesAdj));
5275     else verticesAdj = *verticesAdjSaved;
5276     PetscCall(PetscHSetIGetElems(vhash, &off, verticesAdj));
5277     PetscCall(PetscHSetIDestroy(&vhash));
5278     PetscCheck(off == numVerticesAdj, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Invalid number of local vertices %" PetscInt_FMT " should be %" PetscInt_FMT, off, numVerticesAdj);
5279   }
5280   PetscCall(PetscSortInt(numVerticesAdj, verticesAdj));
5281   /* Create cones */
5282   PetscCall(DMPlexSetChart(dm, 0, numCells + numVerticesAdj));
5283   for (c = 0; c < numCells; ++c) PetscCall(DMPlexSetConeSize(dm, c, numCorners));
5284   PetscCall(DMSetUp(dm));
5285   PetscCall(DMPlexGetCones(dm, &cones));
5286   for (c = 0; c < numCells; ++c) {
5287     for (p = 0; p < numCorners; ++p) {
5288       const PetscInt gv = cells[c * numCorners + p];
5289       PetscInt       lv;
5290 
5291       /* Positions within verticesAdj form 0-based local vertex numbering;
5292          we need to shift it by numCells to get correct DAG points (cells go first) */
5293       PetscCall(PetscFindInt(gv, numVerticesAdj, verticesAdj, &lv));
5294       PetscCheck(lv >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Could not find global vertex %" PetscInt_FMT " in local connectivity", gv);
5295       cones[c * numCorners + p] = lv + numCells;
5296     }
5297   }
5298   /* Build point sf */
5299   PetscCall(PetscLayoutCreate(PetscObjectComm((PetscObject)dm), &layout));
5300   PetscCall(PetscLayoutSetSize(layout, NVertices));
5301   PetscCall(PetscLayoutSetLocalSize(layout, numVertices));
5302   PetscCall(PetscLayoutSetBlockSize(layout, 1));
5303   PetscCall(PetscSFCreateByMatchingIndices(layout, numVerticesAdj, verticesAdj, NULL, numCells, numVerticesAdj, verticesAdj, NULL, numCells, vertexSF, &sfPoint));
5304   PetscCall(PetscLayoutDestroy(&layout));
5305   if (!verticesAdjSaved) PetscCall(PetscFree(verticesAdj));
5306   PetscCall(PetscObjectSetName((PetscObject)sfPoint, "point SF"));
5307   if (dm->sf) {
5308     const char *prefix;
5309 
5310     PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm->sf, &prefix));
5311     PetscCall(PetscObjectSetOptionsPrefix((PetscObject)sfPoint, prefix));
5312   }
5313   PetscCall(DMSetPointSF(dm, sfPoint));
5314   PetscCall(PetscSFDestroy(&sfPoint));
5315   if (vertexSF) PetscCall(PetscObjectSetName((PetscObject)*vertexSF, "Vertex Ownership SF"));
5316   /* Fill in the rest of the topology structure */
5317   PetscCall(DMPlexSymmetrize(dm));
5318   PetscCall(DMPlexStratify(dm));
5319   PetscCall(PetscLogEventEnd(DMPLEX_BuildFromCellList, dm, 0, 0, 0));
5320   PetscFunctionReturn(PETSC_SUCCESS);
5321 }
5322 
5323 /*@
5324   DMPlexBuildCoordinatesFromCellListParallel - Build `DM` coordinates from a list of coordinates for each owned vertex (common mesh generator output)
5325 
5326   Collective; No Fortran Support
5327 
5328   Input Parameters:
5329 + dm           - The `DM`
5330 . spaceDim     - The spatial dimension used for coordinates
5331 . sfVert       - `PetscSF` describing complete vertex ownership
5332 - vertexCoords - An array of numVertices*spaceDim numbers, the coordinates of each vertex
5333 
5334   Level: advanced
5335 
5336 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexBuildCoordinatesFromCellList()`, `DMPlexCreateFromCellListParallelPetsc()`, `DMPlexBuildFromCellListParallel()`
5337 @*/
5338 PetscErrorCode DMPlexBuildCoordinatesFromCellListParallel(DM dm, PetscInt spaceDim, PetscSF sfVert, const PetscReal vertexCoords[])
5339 {
5340   PetscSection coordSection;
5341   Vec          coordinates;
5342   PetscScalar *coords;
5343   PetscInt     numVertices, numVerticesAdj, coordSize, v, vStart, vEnd;
5344 
5345   PetscFunctionBegin;
5346   PetscCall(PetscLogEventBegin(DMPLEX_BuildCoordinatesFromCellList, dm, 0, 0, 0));
5347   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
5348   PetscCheck(vStart >= 0 && vEnd >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "DM is not set up properly. DMPlexBuildFromCellList() should be called first.");
5349   PetscCall(DMSetCoordinateDim(dm, spaceDim));
5350   PetscCall(PetscSFGetGraph(sfVert, &numVertices, &numVerticesAdj, NULL, NULL));
5351   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);
5352   PetscCall(DMGetCoordinateSection(dm, &coordSection));
5353   PetscCall(PetscSectionSetNumFields(coordSection, 1));
5354   PetscCall(PetscSectionSetFieldComponents(coordSection, 0, spaceDim));
5355   PetscCall(PetscSectionSetChart(coordSection, vStart, vEnd));
5356   for (v = vStart; v < vEnd; ++v) {
5357     PetscCall(PetscSectionSetDof(coordSection, v, spaceDim));
5358     PetscCall(PetscSectionSetFieldDof(coordSection, v, 0, spaceDim));
5359   }
5360   PetscCall(PetscSectionSetUp(coordSection));
5361   PetscCall(PetscSectionGetStorageSize(coordSection, &coordSize));
5362   PetscCall(VecCreate(PetscObjectComm((PetscObject)dm), &coordinates));
5363   PetscCall(VecSetBlockSize(coordinates, spaceDim));
5364   PetscCall(PetscObjectSetName((PetscObject)coordinates, "coordinates"));
5365   PetscCall(VecSetSizes(coordinates, coordSize, PETSC_DETERMINE));
5366   PetscCall(VecSetType(coordinates, VECSTANDARD));
5367   PetscCall(VecGetArray(coordinates, &coords));
5368   {
5369     MPI_Datatype coordtype;
5370 
5371     /* Need a temp buffer for coords if we have complex/single */
5372     PetscCallMPI(MPI_Type_contiguous(spaceDim, MPIU_SCALAR, &coordtype));
5373     PetscCallMPI(MPI_Type_commit(&coordtype));
5374 #if defined(PETSC_USE_COMPLEX)
5375     {
5376       PetscScalar *svertexCoords;
5377       PetscInt     i;
5378       PetscCall(PetscMalloc1(numVertices * spaceDim, &svertexCoords));
5379       for (i = 0; i < numVertices * spaceDim; i++) svertexCoords[i] = vertexCoords[i];
5380       PetscCall(PetscSFBcastBegin(sfVert, coordtype, svertexCoords, coords, MPI_REPLACE));
5381       PetscCall(PetscSFBcastEnd(sfVert, coordtype, svertexCoords, coords, MPI_REPLACE));
5382       PetscCall(PetscFree(svertexCoords));
5383     }
5384 #else
5385     PetscCall(PetscSFBcastBegin(sfVert, coordtype, vertexCoords, coords, MPI_REPLACE));
5386     PetscCall(PetscSFBcastEnd(sfVert, coordtype, vertexCoords, coords, MPI_REPLACE));
5387 #endif
5388     PetscCallMPI(MPI_Type_free(&coordtype));
5389   }
5390   PetscCall(VecRestoreArray(coordinates, &coords));
5391   PetscCall(DMSetCoordinatesLocal(dm, coordinates));
5392   PetscCall(VecDestroy(&coordinates));
5393   PetscCall(PetscLogEventEnd(DMPLEX_BuildCoordinatesFromCellList, dm, 0, 0, 0));
5394   PetscFunctionReturn(PETSC_SUCCESS);
5395 }
5396 
5397 /*@
5398   DMPlexCreateFromCellListParallelPetsc - Create distributed `DMPLEX` from a list of vertices for each cell (common mesh generator output)
5399 
5400   Collective
5401 
5402   Input Parameters:
5403 + comm         - The communicator
5404 . dim          - The topological dimension of the mesh
5405 . numCells     - The number of cells owned by this process
5406 . numVertices  - The number of vertices owned by this process, or `PETSC_DECIDE`
5407 . NVertices    - The global number of vertices, or `PETSC_DECIDE`
5408 . numCorners   - The number of vertices for each cell
5409 . interpolate  - Flag indicating that intermediate mesh entities (faces, edges) should be created automatically
5410 . cells        - An array of numCells*numCorners numbers, the global vertex numbers for each cell
5411 . spaceDim     - The spatial dimension used for coordinates
5412 - vertexCoords - An array of numVertices*spaceDim numbers, the coordinates of each vertex
5413 
5414   Output Parameters:
5415 + dm          - The `DM`
5416 . vertexSF    - (Optional) `PetscSF` describing complete vertex ownership
5417 - verticesAdj - (Optional) vertex adjacency array
5418 
5419   Level: intermediate
5420 
5421   Notes:
5422   This function is just a convenient sequence of `DMCreate()`, `DMSetType()`, `DMSetDimension()`,
5423   `DMPlexBuildFromCellListParallel()`, `DMPlexInterpolate()`, `DMPlexBuildCoordinatesFromCellListParallel()`
5424 
5425   See `DMPlexBuildFromCellListParallel()` for an example and details about the topology-related parameters.
5426 
5427   See `DMPlexBuildCoordinatesFromCellListParallel()` for details about the geometry-related parameters.
5428 
5429 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateFromCellListPetsc()`, `DMPlexBuildFromCellListParallel()`, `DMPlexBuildCoordinatesFromCellListParallel()`, `DMPlexCreateFromDAG()`, `DMPlexCreate()`
5430 @*/
5431 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)
5432 {
5433   PetscSF sfVert;
5434 
5435   PetscFunctionBegin;
5436   PetscCall(DMCreate(comm, dm));
5437   PetscCall(DMSetType(*dm, DMPLEX));
5438   PetscValidLogicalCollectiveInt(*dm, dim, 2);
5439   PetscValidLogicalCollectiveInt(*dm, spaceDim, 9);
5440   PetscCall(DMSetDimension(*dm, dim));
5441   PetscCall(DMPlexBuildFromCellListParallel(*dm, numCells, numVertices, NVertices, numCorners, cells, &sfVert, verticesAdj));
5442   if (interpolate) {
5443     DM idm;
5444 
5445     PetscCall(DMPlexInterpolate(*dm, &idm));
5446     PetscCall(DMDestroy(dm));
5447     *dm = idm;
5448   }
5449   PetscCall(DMPlexBuildCoordinatesFromCellListParallel(*dm, spaceDim, sfVert, vertexCoords));
5450   if (vertexSF) *vertexSF = sfVert;
5451   else PetscCall(PetscSFDestroy(&sfVert));
5452   PetscFunctionReturn(PETSC_SUCCESS);
5453 }
5454 
5455 /*@
5456   DMPlexBuildFromCellList - Build `DMPLEX` topology from a list of vertices for each cell (common mesh generator output)
5457 
5458   Collective; No Fortran Support
5459 
5460   Input Parameters:
5461 + dm          - The `DM`
5462 . numCells    - The number of cells owned by this process
5463 . numVertices - The number of vertices owned by this process, or `PETSC_DETERMINE`
5464 . numCorners  - The number of vertices for each cell
5465 - cells       - An array of `numCells` x `numCorners` numbers, the global vertex numbers for each cell
5466 
5467   Level: advanced
5468 
5469   Notes:
5470   Two triangles sharing a face
5471 .vb
5472 
5473         2
5474       / | \
5475      /  |  \
5476     /   |   \
5477    0  0 | 1  3
5478     \   |   /
5479      \  |  /
5480       \ | /
5481         1
5482 .ve
5483   would have input
5484 .vb
5485   numCells = 2, numVertices = 4
5486   cells = [0 1 2  1 3 2]
5487 .ve
5488   which would result in the `DMPLEX`
5489 .vb
5490 
5491         4
5492       / | \
5493      /  |  \
5494     /   |   \
5495    2  0 | 1  5
5496     \   |   /
5497      \  |  /
5498       \ | /
5499         3
5500 .ve
5501 
5502   If numVertices is `PETSC_DETERMINE`, it is computed by PETSc as the maximum vertex index in cells + 1.
5503 
5504 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexBuildFromCellListParallel()`, `DMPlexBuildCoordinatesFromCellList()`, `DMPlexCreateFromCellListPetsc()`
5505 @*/
5506 PetscErrorCode DMPlexBuildFromCellList(DM dm, PetscInt numCells, PetscInt numVertices, PetscInt numCorners, const PetscInt cells[])
5507 {
5508   PetscInt *cones, c, p, dim;
5509 
5510   PetscFunctionBegin;
5511   PetscCall(PetscLogEventBegin(DMPLEX_BuildFromCellList, dm, 0, 0, 0));
5512   PetscCall(DMGetDimension(dm, &dim));
5513   /* Get/check global number of vertices */
5514   {
5515     PetscInt       NVerticesInCells, i;
5516     const PetscInt len = numCells * numCorners;
5517 
5518     /* NVerticesInCells = max(cells) + 1 */
5519     NVerticesInCells = PETSC_MIN_INT;
5520     for (i = 0; i < len; i++)
5521       if (cells[i] > NVerticesInCells) NVerticesInCells = cells[i];
5522     ++NVerticesInCells;
5523 
5524     if (numVertices == PETSC_DECIDE) numVertices = NVerticesInCells;
5525     else
5526       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);
5527   }
5528   PetscCall(DMPlexSetChart(dm, 0, numCells + numVertices));
5529   for (c = 0; c < numCells; ++c) PetscCall(DMPlexSetConeSize(dm, c, numCorners));
5530   PetscCall(DMSetUp(dm));
5531   PetscCall(DMPlexGetCones(dm, &cones));
5532   for (c = 0; c < numCells; ++c) {
5533     for (p = 0; p < numCorners; ++p) cones[c * numCorners + p] = cells[c * numCorners + p] + numCells;
5534   }
5535   PetscCall(DMPlexSymmetrize(dm));
5536   PetscCall(DMPlexStratify(dm));
5537   PetscCall(PetscLogEventEnd(DMPLEX_BuildFromCellList, dm, 0, 0, 0));
5538   PetscFunctionReturn(PETSC_SUCCESS);
5539 }
5540 
5541 /*@
5542   DMPlexBuildCoordinatesFromCellList - Build `DM` coordinates from a list of coordinates for each owned vertex (common mesh generator output)
5543 
5544   Collective
5545 
5546   Input Parameters:
5547 + dm           - The `DM`
5548 . spaceDim     - The spatial dimension used for coordinates
5549 - vertexCoords - An array of numVertices*spaceDim numbers, the coordinates of each vertex
5550 
5551   Level: advanced
5552 
5553 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexBuildCoordinatesFromCellListParallel()`, `DMPlexCreateFromCellListPetsc()`, `DMPlexBuildFromCellList()`
5554 @*/
5555 PetscErrorCode DMPlexBuildCoordinatesFromCellList(DM dm, PetscInt spaceDim, const PetscReal vertexCoords[])
5556 {
5557   PetscSection coordSection;
5558   Vec          coordinates;
5559   DM           cdm;
5560   PetscScalar *coords;
5561   PetscInt     v, vStart, vEnd, d;
5562 
5563   PetscFunctionBegin;
5564   PetscCall(PetscLogEventBegin(DMPLEX_BuildCoordinatesFromCellList, dm, 0, 0, 0));
5565   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
5566   PetscCheck(vStart >= 0 && vEnd >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "DM is not set up properly. DMPlexBuildFromCellList() should be called first.");
5567   PetscCall(DMSetCoordinateDim(dm, spaceDim));
5568   PetscCall(DMGetCoordinateSection(dm, &coordSection));
5569   PetscCall(PetscSectionSetNumFields(coordSection, 1));
5570   PetscCall(PetscSectionSetFieldComponents(coordSection, 0, spaceDim));
5571   PetscCall(PetscSectionSetChart(coordSection, vStart, vEnd));
5572   for (v = vStart; v < vEnd; ++v) {
5573     PetscCall(PetscSectionSetDof(coordSection, v, spaceDim));
5574     PetscCall(PetscSectionSetFieldDof(coordSection, v, 0, spaceDim));
5575   }
5576   PetscCall(PetscSectionSetUp(coordSection));
5577 
5578   PetscCall(DMGetCoordinateDM(dm, &cdm));
5579   PetscCall(DMCreateLocalVector(cdm, &coordinates));
5580   PetscCall(VecSetBlockSize(coordinates, spaceDim));
5581   PetscCall(PetscObjectSetName((PetscObject)coordinates, "coordinates"));
5582   PetscCall(VecGetArrayWrite(coordinates, &coords));
5583   for (v = 0; v < vEnd - vStart; ++v) {
5584     for (d = 0; d < spaceDim; ++d) coords[v * spaceDim + d] = vertexCoords[v * spaceDim + d];
5585   }
5586   PetscCall(VecRestoreArrayWrite(coordinates, &coords));
5587   PetscCall(DMSetCoordinatesLocal(dm, coordinates));
5588   PetscCall(VecDestroy(&coordinates));
5589   PetscCall(PetscLogEventEnd(DMPLEX_BuildCoordinatesFromCellList, dm, 0, 0, 0));
5590   PetscFunctionReturn(PETSC_SUCCESS);
5591 }
5592 
5593 /*@
5594   DMPlexCreateFromCellListPetsc - Create `DMPLEX` from a list of vertices for each cell (common mesh generator output), but only process 0 takes in the input
5595 
5596   Collective
5597 
5598   Input Parameters:
5599 + comm         - The communicator
5600 . dim          - The topological dimension of the mesh
5601 . numCells     - The number of cells, only on process 0
5602 . numVertices  - The number of vertices owned by this process, or `PETSC_DECIDE`, only on process 0
5603 . numCorners   - The number of vertices for each cell, only on process 0
5604 . interpolate  - Flag indicating that intermediate mesh entities (faces, edges) should be created automatically
5605 . cells        - An array of numCells*numCorners numbers, the vertices for each cell, only on process 0
5606 . spaceDim     - The spatial dimension used for coordinates
5607 - vertexCoords - An array of numVertices*spaceDim numbers, the coordinates of each vertex, only on process 0
5608 
5609   Output Parameter:
5610 . dm - The `DM`, which only has points on process 0
5611 
5612   Level: intermediate
5613 
5614   Notes:
5615   This function is just a convenient sequence of `DMCreate()`, `DMSetType()`, `DMSetDimension()`, `DMPlexBuildFromCellList()`,
5616   `DMPlexInterpolate()`, `DMPlexBuildCoordinatesFromCellList()`
5617 
5618   See `DMPlexBuildFromCellList()` for an example and details about the topology-related parameters.
5619   See `DMPlexBuildCoordinatesFromCellList()` for details about the geometry-related parameters.
5620   See `DMPlexCreateFromCellListParallelPetsc()` for parallel input
5621 
5622 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateFromCellListParallelPetsc()`, `DMPlexBuildFromCellList()`, `DMPlexBuildCoordinatesFromCellList()`, `DMPlexCreateFromDAG()`, `DMPlexCreate()`
5623 @*/
5624 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)
5625 {
5626   PetscMPIInt rank;
5627 
5628   PetscFunctionBegin;
5629   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.");
5630   PetscCallMPI(MPI_Comm_rank(comm, &rank));
5631   PetscCall(DMCreate(comm, dm));
5632   PetscCall(DMSetType(*dm, DMPLEX));
5633   PetscCall(DMSetDimension(*dm, dim));
5634   if (rank == 0) PetscCall(DMPlexBuildFromCellList(*dm, numCells, numVertices, numCorners, cells));
5635   else PetscCall(DMPlexBuildFromCellList(*dm, 0, 0, 0, NULL));
5636   if (interpolate) {
5637     DM idm;
5638 
5639     PetscCall(DMPlexInterpolate(*dm, &idm));
5640     PetscCall(DMDestroy(dm));
5641     *dm = idm;
5642   }
5643   if (rank == 0) PetscCall(DMPlexBuildCoordinatesFromCellList(*dm, spaceDim, vertexCoords));
5644   else PetscCall(DMPlexBuildCoordinatesFromCellList(*dm, spaceDim, NULL));
5645   PetscFunctionReturn(PETSC_SUCCESS);
5646 }
5647 
5648 /*@
5649   DMPlexCreateFromDAG - This takes as input the adjacency-list representation of the Directed Acyclic Graph (Hasse Diagram) encoding a mesh, and produces a `DM`
5650 
5651   Input Parameters:
5652 + dm               - The empty `DM` object, usually from `DMCreate()` and `DMSetDimension()`
5653 . depth            - The depth of the DAG
5654 . numPoints        - Array of size depth + 1 containing the number of points at each `depth`
5655 . coneSize         - The cone size of each point
5656 . cones            - The concatenation of the cone points for each point, the cone list must be oriented correctly for each point
5657 . coneOrientations - The orientation of each cone point
5658 - vertexCoords     - An array of `numPoints`[0]*spacedim numbers representing the coordinates of each vertex, with spacedim the value set via `DMSetCoordinateDim()`
5659 
5660   Output Parameter:
5661 . dm - The `DM`
5662 
5663   Level: advanced
5664 
5665   Note:
5666   Two triangles sharing a face would have input
5667 .vb
5668   depth = 1, numPoints = [4 2], coneSize = [3 3 0 0 0 0]
5669   cones = [2 3 4  3 5 4], coneOrientations = [0 0 0  0 0 0]
5670  vertexCoords = [-1.0 0.0  0.0 -1.0  0.0 1.0  1.0 0.0]
5671 .ve
5672   which would result in the DMPlex
5673 .vb
5674         4
5675       / | \
5676      /  |  \
5677     /   |   \
5678    2  0 | 1  5
5679     \   |   /
5680      \  |  /
5681       \ | /
5682         3
5683 .ve
5684   Notice that all points are numbered consecutively, unlike `DMPlexCreateFromCellListPetsc()`
5685 
5686 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateFromCellListPetsc()`, `DMPlexCreate()`
5687 @*/
5688 PetscErrorCode DMPlexCreateFromDAG(DM dm, PetscInt depth, const PetscInt numPoints[], const PetscInt coneSize[], const PetscInt cones[], const PetscInt coneOrientations[], const PetscScalar vertexCoords[])
5689 {
5690   Vec          coordinates;
5691   PetscSection coordSection;
5692   PetscScalar *coords;
5693   PetscInt     coordSize, firstVertex = -1, pStart = 0, pEnd = 0, p, v, dim, dimEmbed, d, off;
5694 
5695   PetscFunctionBegin;
5696   PetscCall(DMGetDimension(dm, &dim));
5697   PetscCall(DMGetCoordinateDim(dm, &dimEmbed));
5698   PetscCheck(dimEmbed >= dim, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Embedding dimension %" PetscInt_FMT " cannot be less than intrinsic dimension %" PetscInt_FMT, dimEmbed, dim);
5699   for (d = 0; d <= depth; ++d) pEnd += numPoints[d];
5700   PetscCall(DMPlexSetChart(dm, pStart, pEnd));
5701   for (p = pStart; p < pEnd; ++p) {
5702     PetscCall(DMPlexSetConeSize(dm, p, coneSize[p - pStart]));
5703     if (firstVertex < 0 && !coneSize[p - pStart]) firstVertex = p - pStart;
5704   }
5705   PetscCheck(firstVertex >= 0 || !numPoints[0], PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Expected %" PetscInt_FMT " vertices but could not find any", numPoints[0]);
5706   PetscCall(DMSetUp(dm)); /* Allocate space for cones */
5707   for (p = pStart, off = 0; p < pEnd; off += coneSize[p - pStart], ++p) {
5708     PetscCall(DMPlexSetCone(dm, p, &cones[off]));
5709     PetscCall(DMPlexSetConeOrientation(dm, p, &coneOrientations[off]));
5710   }
5711   PetscCall(DMPlexSymmetrize(dm));
5712   PetscCall(DMPlexStratify(dm));
5713   /* Build coordinates */
5714   PetscCall(DMGetCoordinateSection(dm, &coordSection));
5715   PetscCall(PetscSectionSetNumFields(coordSection, 1));
5716   PetscCall(PetscSectionSetFieldComponents(coordSection, 0, dimEmbed));
5717   PetscCall(PetscSectionSetChart(coordSection, firstVertex, firstVertex + numPoints[0]));
5718   for (v = firstVertex; v < firstVertex + numPoints[0]; ++v) {
5719     PetscCall(PetscSectionSetDof(coordSection, v, dimEmbed));
5720     PetscCall(PetscSectionSetFieldDof(coordSection, v, 0, dimEmbed));
5721   }
5722   PetscCall(PetscSectionSetUp(coordSection));
5723   PetscCall(PetscSectionGetStorageSize(coordSection, &coordSize));
5724   PetscCall(VecCreate(PETSC_COMM_SELF, &coordinates));
5725   PetscCall(PetscObjectSetName((PetscObject)coordinates, "coordinates"));
5726   PetscCall(VecSetSizes(coordinates, coordSize, PETSC_DETERMINE));
5727   PetscCall(VecSetBlockSize(coordinates, dimEmbed));
5728   PetscCall(VecSetType(coordinates, VECSTANDARD));
5729   if (vertexCoords) {
5730     PetscCall(VecGetArray(coordinates, &coords));
5731     for (v = 0; v < numPoints[0]; ++v) {
5732       PetscInt off;
5733 
5734       PetscCall(PetscSectionGetOffset(coordSection, v + firstVertex, &off));
5735       for (d = 0; d < dimEmbed; ++d) coords[off + d] = vertexCoords[v * dimEmbed + d];
5736     }
5737   }
5738   PetscCall(VecRestoreArray(coordinates, &coords));
5739   PetscCall(DMSetCoordinatesLocal(dm, coordinates));
5740   PetscCall(VecDestroy(&coordinates));
5741   PetscFunctionReturn(PETSC_SUCCESS);
5742 }
5743 
5744 /*
5745   DMPlexCreateCellVertexFromFile - Create a `DMPLEX` mesh from a simple cell-vertex file.
5746 
5747   Collective
5748 
5749 + comm        - The MPI communicator
5750 . filename    - Name of the .dat file
5751 - interpolate - Create faces and edges in the mesh
5752 
5753   Output Parameter:
5754 . dm  - The `DM` object representing the mesh
5755 
5756   Level: beginner
5757 
5758   Note:
5759   The format is the simplest possible:
5760 .vb
5761   dim Ne Nv Nc Nl
5762   v_1 v_2 ... v_Nc
5763   ...
5764   x y z marker_1 ... marker_Nl
5765 .ve
5766 
5767   Developer Note:
5768   Should use a `PetscViewer` not a filename
5769 
5770 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateFromFile()`, `DMPlexCreateGmsh()`, `DMPlexCreate()`
5771 */
5772 static PetscErrorCode DMPlexCreateCellVertexFromFile(MPI_Comm comm, const char filename[], PetscBool interpolate, DM *dm)
5773 {
5774   DMLabel      marker;
5775   PetscViewer  viewer;
5776   Vec          coordinates;
5777   PetscSection coordSection;
5778   PetscScalar *coords;
5779   char         line[PETSC_MAX_PATH_LEN];
5780   PetscInt     cdim, coordSize, v, c, d;
5781   PetscMPIInt  rank;
5782   int          snum, dim, Nv, Nc, Ncn, Nl;
5783 
5784   PetscFunctionBegin;
5785   PetscCallMPI(MPI_Comm_rank(comm, &rank));
5786   PetscCall(PetscViewerCreate(comm, &viewer));
5787   PetscCall(PetscViewerSetType(viewer, PETSCVIEWERASCII));
5788   PetscCall(PetscViewerFileSetMode(viewer, FILE_MODE_READ));
5789   PetscCall(PetscViewerFileSetName(viewer, filename));
5790   if (rank == 0) {
5791     PetscCall(PetscViewerRead(viewer, line, 5, NULL, PETSC_STRING));
5792     snum = sscanf(line, "%d %d %d %d %d", &dim, &Nc, &Nv, &Ncn, &Nl);
5793     PetscCheck(snum == 5, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unable to parse cell-vertex file: %s", line);
5794   } else {
5795     Nc = Nv = Ncn = Nl = 0;
5796   }
5797   PetscCallMPI(MPI_Bcast(&dim, 1, MPI_INT, 0, comm));
5798   cdim = (PetscInt)dim;
5799   PetscCall(DMCreate(comm, dm));
5800   PetscCall(DMSetType(*dm, DMPLEX));
5801   PetscCall(DMPlexSetChart(*dm, 0, Nc + Nv));
5802   PetscCall(DMSetDimension(*dm, (PetscInt)dim));
5803   PetscCall(DMSetCoordinateDim(*dm, cdim));
5804   /* Read topology */
5805   if (rank == 0) {
5806     char     format[PETSC_MAX_PATH_LEN];
5807     PetscInt cone[8];
5808     int      vbuf[8], v;
5809 
5810     for (c = 0; c < Ncn; ++c) {
5811       format[c * 3 + 0] = '%';
5812       format[c * 3 + 1] = 'd';
5813       format[c * 3 + 2] = ' ';
5814     }
5815     format[Ncn * 3 - 1] = '\0';
5816     for (c = 0; c < Nc; ++c) PetscCall(DMPlexSetConeSize(*dm, c, Ncn));
5817     PetscCall(DMSetUp(*dm));
5818     for (c = 0; c < Nc; ++c) {
5819       PetscCall(PetscViewerRead(viewer, line, Ncn, NULL, PETSC_STRING));
5820       switch (Ncn) {
5821       case 2:
5822         snum = sscanf(line, format, &vbuf[0], &vbuf[1]);
5823         break;
5824       case 3:
5825         snum = sscanf(line, format, &vbuf[0], &vbuf[1], &vbuf[2]);
5826         break;
5827       case 4:
5828         snum = sscanf(line, format, &vbuf[0], &vbuf[1], &vbuf[2], &vbuf[3]);
5829         break;
5830       case 6:
5831         snum = sscanf(line, format, &vbuf[0], &vbuf[1], &vbuf[2], &vbuf[3], &vbuf[4], &vbuf[5]);
5832         break;
5833       case 8:
5834         snum = sscanf(line, format, &vbuf[0], &vbuf[1], &vbuf[2], &vbuf[3], &vbuf[4], &vbuf[5], &vbuf[6], &vbuf[7]);
5835         break;
5836       default:
5837         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "No cell shape with %d vertices", Ncn);
5838       }
5839       PetscCheck(snum == Ncn, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unable to parse cell-vertex file: %s", line);
5840       for (v = 0; v < Ncn; ++v) cone[v] = vbuf[v] + Nc;
5841       /* Hexahedra are inverted */
5842       if (Ncn == 8) {
5843         PetscInt tmp = cone[1];
5844         cone[1]      = cone[3];
5845         cone[3]      = tmp;
5846       }
5847       PetscCall(DMPlexSetCone(*dm, c, cone));
5848     }
5849   }
5850   PetscCall(DMPlexSymmetrize(*dm));
5851   PetscCall(DMPlexStratify(*dm));
5852   /* Read coordinates */
5853   PetscCall(DMGetCoordinateSection(*dm, &coordSection));
5854   PetscCall(PetscSectionSetNumFields(coordSection, 1));
5855   PetscCall(PetscSectionSetFieldComponents(coordSection, 0, cdim));
5856   PetscCall(PetscSectionSetChart(coordSection, Nc, Nc + Nv));
5857   for (v = Nc; v < Nc + Nv; ++v) {
5858     PetscCall(PetscSectionSetDof(coordSection, v, cdim));
5859     PetscCall(PetscSectionSetFieldDof(coordSection, v, 0, cdim));
5860   }
5861   PetscCall(PetscSectionSetUp(coordSection));
5862   PetscCall(PetscSectionGetStorageSize(coordSection, &coordSize));
5863   PetscCall(VecCreate(PETSC_COMM_SELF, &coordinates));
5864   PetscCall(PetscObjectSetName((PetscObject)coordinates, "coordinates"));
5865   PetscCall(VecSetSizes(coordinates, coordSize, PETSC_DETERMINE));
5866   PetscCall(VecSetBlockSize(coordinates, cdim));
5867   PetscCall(VecSetType(coordinates, VECSTANDARD));
5868   PetscCall(VecGetArray(coordinates, &coords));
5869   if (rank == 0) {
5870     char   format[PETSC_MAX_PATH_LEN];
5871     double x[3];
5872     int    l, val[3];
5873 
5874     if (Nl) {
5875       for (l = 0; l < Nl; ++l) {
5876         format[l * 3 + 0] = '%';
5877         format[l * 3 + 1] = 'd';
5878         format[l * 3 + 2] = ' ';
5879       }
5880       format[Nl * 3 - 1] = '\0';
5881       PetscCall(DMCreateLabel(*dm, "marker"));
5882       PetscCall(DMGetLabel(*dm, "marker", &marker));
5883     }
5884     for (v = 0; v < Nv; ++v) {
5885       PetscCall(PetscViewerRead(viewer, line, 3 + Nl, NULL, PETSC_STRING));
5886       snum = sscanf(line, "%lg %lg %lg", &x[0], &x[1], &x[2]);
5887       PetscCheck(snum == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unable to parse cell-vertex file: %s", line);
5888       switch (Nl) {
5889       case 0:
5890         snum = 0;
5891         break;
5892       case 1:
5893         snum = sscanf(line, format, &val[0]);
5894         break;
5895       case 2:
5896         snum = sscanf(line, format, &val[0], &val[1]);
5897         break;
5898       case 3:
5899         snum = sscanf(line, format, &val[0], &val[1], &val[2]);
5900         break;
5901       default:
5902         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Request support for %d labels", Nl);
5903       }
5904       PetscCheck(snum == Nl, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unable to parse cell-vertex file: %s", line);
5905       for (d = 0; d < cdim; ++d) coords[v * cdim + d] = x[d];
5906       for (l = 0; l < Nl; ++l) PetscCall(DMLabelSetValue(marker, v + Nc, val[l]));
5907     }
5908   }
5909   PetscCall(VecRestoreArray(coordinates, &coords));
5910   PetscCall(DMSetCoordinatesLocal(*dm, coordinates));
5911   PetscCall(VecDestroy(&coordinates));
5912   PetscCall(PetscViewerDestroy(&viewer));
5913   if (interpolate) {
5914     DM      idm;
5915     DMLabel bdlabel;
5916 
5917     PetscCall(DMPlexInterpolate(*dm, &idm));
5918     PetscCall(DMDestroy(dm));
5919     *dm = idm;
5920 
5921     if (!Nl) {
5922       PetscCall(DMCreateLabel(*dm, "marker"));
5923       PetscCall(DMGetLabel(*dm, "marker", &bdlabel));
5924       PetscCall(DMPlexMarkBoundaryFaces(*dm, PETSC_DETERMINE, bdlabel));
5925       PetscCall(DMPlexLabelComplete(*dm, bdlabel));
5926     }
5927   }
5928   PetscFunctionReturn(PETSC_SUCCESS);
5929 }
5930 
5931 /*@
5932   DMPlexCreateFromFile - This takes a filename and produces a `DM`
5933 
5934   Collective
5935 
5936   Input Parameters:
5937 + comm        - The communicator
5938 . filename    - A file name
5939 . plexname    - The object name of the resulting `DM`, also used for intra-datafile lookup by some formats
5940 - interpolate - Flag to create intermediate mesh pieces (edges, faces)
5941 
5942   Output Parameter:
5943 . dm - The `DM`
5944 
5945   Options Database Key:
5946 . -dm_plex_create_from_hdf5_xdmf - use the `PETSC_VIEWER_HDF5_XDMF` format for reading HDF5
5947 
5948   Use `-dm_plex_create_ prefix` to pass options to the internal `PetscViewer`, e.g.
5949 $ -dm_plex_create_viewer_hdf5_collective
5950 
5951   Level: beginner
5952 
5953   Notes:
5954   Using `PETSCVIEWERHDF5` type with `PETSC_VIEWER_HDF5_PETSC` format, one can save multiple `DMPLEX`
5955   meshes in a single HDF5 file. This in turn requires one to name the `DMPLEX` object with `PetscObjectSetName()`
5956   before saving it with `DMView()` and before loading it with `DMLoad()` for identification of the mesh object.
5957   The input parameter name is thus used to name the `DMPLEX` object when `DMPlexCreateFromFile()` internally
5958   calls `DMLoad()`. Currently, name is ignored for other viewer types and/or formats.
5959 
5960 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateFromDAG()`, `DMPlexCreateFromCellListPetsc()`, `DMPlexCreate()`, `PetscObjectSetName()`, `DMView()`, `DMLoad()`
5961 @*/
5962 PetscErrorCode DMPlexCreateFromFile(MPI_Comm comm, const char filename[], const char plexname[], PetscBool interpolate, DM *dm)
5963 {
5964   const char  extGmsh[]      = ".msh";
5965   const char  extGmsh2[]     = ".msh2";
5966   const char  extGmsh4[]     = ".msh4";
5967   const char  extCGNS[]      = ".cgns";
5968   const char  extExodus[]    = ".exo";
5969   const char  extExodus_e[]  = ".e";
5970   const char  extGenesis[]   = ".gen";
5971   const char  extFluent[]    = ".cas";
5972   const char  extHDF5[]      = ".h5";
5973   const char  extXDMFHDF5[]  = ".xdmf.h5";
5974   const char  extPLY[]       = ".ply";
5975   const char  extEGADSLite[] = ".egadslite";
5976   const char  extEGADS[]     = ".egads";
5977   const char  extIGES[]      = ".igs";
5978   const char  extSTEP[]      = ".stp";
5979   const char  extCV[]        = ".dat";
5980   size_t      len;
5981   PetscBool   isGmsh, isGmsh2, isGmsh4, isCGNS, isExodus, isGenesis, isFluent, isHDF5, isPLY, isEGADSLite, isEGADS, isIGES, isSTEP, isCV, isXDMFHDF5;
5982   PetscMPIInt rank;
5983 
5984   PetscFunctionBegin;
5985   PetscAssertPointer(filename, 2);
5986   if (plexname) PetscAssertPointer(plexname, 3);
5987   PetscAssertPointer(dm, 5);
5988   PetscCall(DMInitializePackage());
5989   PetscCall(PetscLogEventBegin(DMPLEX_CreateFromFile, 0, 0, 0, 0));
5990   PetscCallMPI(MPI_Comm_rank(comm, &rank));
5991   PetscCall(PetscStrlen(filename, &len));
5992   PetscCheck(len, comm, PETSC_ERR_ARG_WRONG, "Filename must be a valid path");
5993 
5994 #define CheckExtension(extension__, is_extension__) \
5995   do { \
5996     PetscAssert(sizeof(extension__), comm, PETSC_ERR_PLIB, "Zero-size extension: %s", extension__); \
5997     /* don't count the null-terminator at the end */ \
5998     const size_t ext_len = sizeof(extension__) - 1; \
5999     if (len < ext_len) { \
6000       is_extension__ = PETSC_FALSE; \
6001     } else { \
6002       PetscCall(PetscStrncmp(filename + len - ext_len, extension__, ext_len, &is_extension__)); \
6003     } \
6004   } while (0)
6005 
6006   CheckExtension(extGmsh, isGmsh);
6007   CheckExtension(extGmsh2, isGmsh2);
6008   CheckExtension(extGmsh4, isGmsh4);
6009   CheckExtension(extCGNS, isCGNS);
6010   CheckExtension(extExodus, isExodus);
6011   if (!isExodus) CheckExtension(extExodus_e, isExodus);
6012   CheckExtension(extGenesis, isGenesis);
6013   CheckExtension(extFluent, isFluent);
6014   CheckExtension(extHDF5, isHDF5);
6015   CheckExtension(extPLY, isPLY);
6016   CheckExtension(extEGADSLite, isEGADSLite);
6017   CheckExtension(extEGADS, isEGADS);
6018   CheckExtension(extIGES, isIGES);
6019   CheckExtension(extSTEP, isSTEP);
6020   CheckExtension(extCV, isCV);
6021   CheckExtension(extXDMFHDF5, isXDMFHDF5);
6022 
6023 #undef CheckExtension
6024 
6025   if (isGmsh || isGmsh2 || isGmsh4) {
6026     PetscCall(DMPlexCreateGmshFromFile(comm, filename, interpolate, dm));
6027   } else if (isCGNS) {
6028     PetscCall(DMPlexCreateCGNSFromFile(comm, filename, interpolate, dm));
6029   } else if (isExodus || isGenesis) {
6030     PetscCall(DMPlexCreateExodusFromFile(comm, filename, interpolate, dm));
6031   } else if (isFluent) {
6032     PetscCall(DMPlexCreateFluentFromFile(comm, filename, interpolate, dm));
6033   } else if (isHDF5) {
6034     PetscViewer viewer;
6035 
6036     /* PETSC_VIEWER_HDF5_XDMF is used if the filename ends with .xdmf.h5, or if -dm_plex_create_from_hdf5_xdmf option is present */
6037     PetscCall(PetscOptionsGetBool(NULL, NULL, "-dm_plex_create_from_hdf5_xdmf", &isXDMFHDF5, NULL));
6038     PetscCall(PetscViewerCreate(comm, &viewer));
6039     PetscCall(PetscViewerSetType(viewer, PETSCVIEWERHDF5));
6040     PetscCall(PetscViewerSetOptionsPrefix(viewer, "dm_plex_create_"));
6041     PetscCall(PetscViewerSetFromOptions(viewer));
6042     PetscCall(PetscViewerFileSetMode(viewer, FILE_MODE_READ));
6043     PetscCall(PetscViewerFileSetName(viewer, filename));
6044 
6045     PetscCall(DMCreate(comm, dm));
6046     PetscCall(PetscObjectSetName((PetscObject)*dm, plexname));
6047     PetscCall(DMSetType(*dm, DMPLEX));
6048     if (isXDMFHDF5) PetscCall(PetscViewerPushFormat(viewer, PETSC_VIEWER_HDF5_XDMF));
6049     PetscCall(DMLoad(*dm, viewer));
6050     if (isXDMFHDF5) PetscCall(PetscViewerPopFormat(viewer));
6051     PetscCall(PetscViewerDestroy(&viewer));
6052 
6053     if (interpolate) {
6054       DM idm;
6055 
6056       PetscCall(DMPlexInterpolate(*dm, &idm));
6057       PetscCall(DMDestroy(dm));
6058       *dm = idm;
6059     }
6060   } else if (isPLY) {
6061     PetscCall(DMPlexCreatePLYFromFile(comm, filename, interpolate, dm));
6062   } else if (isEGADSLite || isEGADS || isIGES || isSTEP) {
6063     if (isEGADSLite) PetscCall(DMPlexCreateEGADSLiteFromFile(comm, filename, dm));
6064     else PetscCall(DMPlexCreateEGADSFromFile(comm, filename, dm));
6065     if (!interpolate) {
6066       DM udm;
6067 
6068       PetscCall(DMPlexUninterpolate(*dm, &udm));
6069       PetscCall(DMDestroy(dm));
6070       *dm = udm;
6071     }
6072   } else if (isCV) {
6073     PetscCall(DMPlexCreateCellVertexFromFile(comm, filename, interpolate, dm));
6074   } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cannot load file %s: unrecognized extension", filename);
6075   PetscCall(PetscStrlen(plexname, &len));
6076   if (len) PetscCall(PetscObjectSetName((PetscObject)*dm, plexname));
6077   PetscCall(PetscLogEventEnd(DMPLEX_CreateFromFile, 0, 0, 0, 0));
6078   PetscFunctionReturn(PETSC_SUCCESS);
6079 }
6080 
6081 /*@
6082   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.
6083 
6084   Input Parameters:
6085 + tr     - The `DMPlexTransform`
6086 - prefix - An options prefix, or NULL
6087 
6088   Output Parameter:
6089 . dm - The `DM`
6090 
6091   Level: beginner
6092 
6093   Notes:
6094   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.
6095 
6096 .seealso: `DMPlexCreateFromFile`, `DMPlexCreateFromDAG()`, `DMPlexCreateFromCellListPetsc()`, `DMPlexCreate()`
6097 @*/
6098 PetscErrorCode DMPlexCreateEphemeral(DMPlexTransform tr, const char prefix[], DM *dm)
6099 {
6100   DM           bdm, bcdm, cdm;
6101   Vec          coordinates, coordinatesNew;
6102   PetscSection cs;
6103   PetscInt     cdim, Nl;
6104 
6105   PetscFunctionBegin;
6106   PetscCall(DMCreate(PetscObjectComm((PetscObject)tr), dm));
6107   PetscCall(DMSetType(*dm, DMPLEX));
6108   ((DM_Plex *)(*dm)->data)->interpolated = DMPLEX_INTERPOLATED_FULL;
6109   // Handle coordinates
6110   PetscCall(DMPlexTransformGetDM(tr, &bdm));
6111   PetscCall(DMPlexTransformSetDimensions(tr, bdm, *dm));
6112   PetscCall(DMGetCoordinateDim(*dm, &cdim));
6113   PetscCall(DMGetCoordinateDM(bdm, &bcdm));
6114   PetscCall(DMGetCoordinateDM(*dm, &cdm));
6115   PetscCall(DMCopyDisc(bcdm, cdm));
6116   PetscCall(DMGetLocalSection(cdm, &cs));
6117   PetscCall(PetscSectionSetNumFields(cs, 1));
6118   PetscCall(PetscSectionSetFieldComponents(cs, 0, cdim));
6119   PetscCall(DMGetCoordinatesLocal(bdm, &coordinates));
6120   PetscCall(VecDuplicate(coordinates, &coordinatesNew));
6121   PetscCall(VecCopy(coordinates, coordinatesNew));
6122   PetscCall(DMSetCoordinatesLocal(*dm, coordinatesNew));
6123   PetscCall(VecDestroy(&coordinatesNew));
6124 
6125   PetscCall(PetscObjectReference((PetscObject)tr));
6126   PetscCall(DMPlexTransformDestroy(&((DM_Plex *)(*dm)->data)->tr));
6127   ((DM_Plex *)(*dm)->data)->tr = tr;
6128   PetscCall(DMPlexDistributeSetDefault(*dm, PETSC_FALSE));
6129   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*dm, prefix));
6130   PetscCall(DMSetFromOptions(*dm));
6131 
6132   PetscCall(DMGetNumLabels(bdm, &Nl));
6133   for (PetscInt l = 0; l < Nl; ++l) {
6134     DMLabel     label, labelNew;
6135     const char *lname;
6136     PetscBool   isDepth, isCellType;
6137 
6138     PetscCall(DMGetLabelName(bdm, l, &lname));
6139     PetscCall(PetscStrcmp(lname, "depth", &isDepth));
6140     if (isDepth) continue;
6141     PetscCall(PetscStrcmp(lname, "celltype", &isCellType));
6142     if (isCellType) continue;
6143     PetscCall(DMCreateLabel(*dm, lname));
6144     PetscCall(DMGetLabel(bdm, lname, &label));
6145     PetscCall(DMGetLabel(*dm, lname, &labelNew));
6146     PetscCall(DMLabelSetType(labelNew, DMLABELEPHEMERAL));
6147     PetscCall(DMLabelEphemeralSetLabel(labelNew, label));
6148     PetscCall(DMLabelEphemeralSetTransform(labelNew, tr));
6149     PetscCall(DMLabelSetUp(labelNew));
6150   }
6151   PetscFunctionReturn(PETSC_SUCCESS);
6152 }
6153