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