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