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