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