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