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