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