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