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