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