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