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