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