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