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