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