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