xref: /petsc/src/dm/impls/plex/plexcreate.c (revision 963ee18c9c475bed7760ce15dba2fb120b1385aa)
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, &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[1024], n = 1024;
5003     char        fulloption[PETSC_MAX_PATH_LEN];
5004     const char *name = &option[14];
5005 
5006     PetscCall(DMCreateLabel(dm, name));
5007     PetscCall(DMGetLabel(dm, name, &label));
5008     fulloption[0] = '-';
5009     fulloption[1] = 0;
5010     PetscCall(PetscStrlcat(fulloption, option, PETSC_MAX_PATH_LEN));
5011     PetscCall(PetscOptionsGetIntArray(NULL, ((PetscObject)dm)->prefix, fulloption, points, &n, NULL));
5012     for (PetscInt p = 0; p < n; ++p) PetscCall(DMLabelSetValue(label, points[p], 1));
5013   }
5014   // Allow cohesive label creation
5015   //   Faces are input, completed, and all points are marked with their depth
5016   PetscCall(PetscOptionsFindPairPrefix_Private(NULL, ((PetscObject)dm)->prefix, "-dm_plex_cohesive_label_", &option, NULL, &flg));
5017   if (flg) {
5018     DMLabel   label;
5019     PetscInt  points[1024], n, pStart, pEnd, Nl = 1;
5020     PetscBool noCreate = PETSC_FALSE;
5021     char      fulloption[PETSC_MAX_PATH_LEN];
5022     char      name[PETSC_MAX_PATH_LEN];
5023     size_t    len;
5024 
5025     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5026     PetscCall(PetscStrncpy(name, &option[23], PETSC_MAX_PATH_LEN));
5027     PetscCall(PetscStrlen(name, &len));
5028     if (name[len - 1] == '0') Nl = 10;
5029     for (PetscInt l = 0; l < Nl; ++l) {
5030       if (l > 0) name[len - 1] = (char)('0' + l);
5031       fulloption[0] = 0;
5032       PetscCall(PetscStrlcat(fulloption, "-dm_plex_cohesive_label_", 32));
5033       PetscCall(PetscStrlcat(fulloption, name, PETSC_MAX_PATH_LEN - 32));
5034       n = 1024;
5035       PetscCall(PetscOptionsGetIntArray(NULL, ((PetscObject)dm)->prefix, fulloption, points, &n, &flg));
5036       if (!flg) break;
5037       PetscCall(DMHasLabel(dm, name, &noCreate));
5038       if (noCreate) {
5039         DMLabel         inlabel;
5040         IS              pointIS;
5041         const PetscInt *lpoints;
5042         PetscInt        pdep, ln, inval = points[0];
5043         char            newname[PETSC_MAX_PATH_LEN];
5044 
5045         PetscCheck(n == 1, comm, PETSC_ERR_ARG_WRONG, "Must specify a label value with this option");
5046         PetscCall(DMGetLabel(dm, name, &inlabel));
5047         PetscCall(DMLabelGetStratumIS(inlabel, inval, &pointIS));
5048         PetscCall(ISGetLocalSize(pointIS, &ln));
5049         PetscCall(ISGetIndices(pointIS, &lpoints));
5050         PetscCall(DMPlexGetPointDepth(dm, lpoints[0], &pdep));
5051         PetscCall(PetscSNPrintf(newname, PETSC_MAX_PATH_LEN, "%s%" PetscInt_FMT, name, points[0]));
5052         PetscCall(DMCreateLabel(dm, newname));
5053         PetscCall(DMGetLabel(dm, newname, &label));
5054         if (!pdep) PetscCall(ProcessCohesiveLabel_Vertices(dm, label, inlabel, inval, ln, lpoints));
5055         else PetscCall(ProcessCohesiveLabel_Faces(dm, label, ln, lpoints));
5056         PetscCall(ISRestoreIndices(pointIS, &lpoints));
5057         PetscCall(ISDestroy(&pointIS));
5058       } else {
5059         PetscCall(DMCreateLabel(dm, name));
5060         PetscCall(DMGetLabel(dm, name, &label));
5061         if (pStart >= pEnd) n = 0;
5062         PetscCall(ProcessCohesiveLabel_Faces(dm, label, n, points));
5063       }
5064       PetscCall(DMPlexOrientLabel(dm, label));
5065       PetscCall(DMPlexLabelCohesiveComplete(dm, label, NULL, 1, PETSC_FALSE, PETSC_FALSE, NULL));
5066     }
5067   }
5068   PetscCall(DMViewFromOptions(dm, NULL, "-created_dm_view"));
5069   PetscCall(PetscLogEventEnd(DMPLEX_CreateFromOptions, dm, 0, 0, 0));
5070   PetscFunctionReturn(PETSC_SUCCESS);
5071 }
5072 
5073 PetscErrorCode DMSetFromOptions_NonRefinement_Plex(DM dm, PetscOptionItems PetscOptionsObject)
5074 {
5075   DM_Plex  *mesh = (DM_Plex *)dm->data;
5076   PetscBool flg, flg2;
5077   char      bdLabel[PETSC_MAX_PATH_LEN];
5078   char      method[PETSC_MAX_PATH_LEN];
5079 
5080   PetscFunctionBegin;
5081   /* Handle viewing */
5082   PetscCall(PetscOptionsBool("-dm_plex_print_set_values", "Output all set values info", "DMPlexMatSetClosure", PETSC_FALSE, &mesh->printSetValues, NULL));
5083   PetscCall(PetscOptionsBoundedInt("-dm_plex_print_fem", "Debug output level for all fem computations", "DMPlexSNESComputeResidualFEM", 0, &mesh->printFEM, NULL, 0));
5084   PetscCall(PetscOptionsBoundedInt("-dm_plex_print_fvm", "Debug output level for all fvm computations", "DMPlexSNESComputeResidualFVM", 0, &mesh->printFVM, NULL, 0));
5085   PetscCall(PetscOptionsReal("-dm_plex_print_tol", "Tolerance for FEM output", "DMPlexSNESComputeResidualFEM", mesh->printTol, &mesh->printTol, NULL));
5086   PetscCall(PetscOptionsBoundedInt("-dm_plex_print_l2", "Debug output level all L2 diff computations", "DMComputeL2Diff", 0, &mesh->printL2, NULL, 0));
5087   PetscCall(PetscOptionsBoundedInt("-dm_plex_print_locate", "Debug output level all point location computations", "DMLocatePoints", 0, &mesh->printLocate, NULL, 0));
5088   PetscCall(PetscOptionsBoundedInt("-dm_plex_print_project", "Debug output level all projection computations", "DMPlexProject", 0, &mesh->printProject, NULL, 0));
5089   PetscCall(DMMonitorSetFromOptions(dm, "-dm_plex_monitor_throughput", "Monitor the simulation throughput", "DMPlexMonitorThroughput", DMPlexMonitorThroughput, NULL, &flg));
5090   if (flg) PetscCall(PetscLogDefaultBegin());
5091   // Interpolation
5092   PetscCall(PetscOptionsBool("-dm_plex_interpolate_prefer_tensor", "When different orderings exist, prefer the tensor order", "DMPlexSetInterpolationPreferTensor", mesh->interpolatePreferTensor, &mesh->interpolatePreferTensor, NULL));
5093   /* Labeling */
5094   PetscCall(PetscOptionsString("-dm_plex_boundary_label", "Label to mark the mesh boundary", "", bdLabel, bdLabel, sizeof(bdLabel), &flg));
5095   if (flg) PetscCall(DMPlexCreateBoundaryLabel_Private(dm, bdLabel));
5096   /* Point Location */
5097   PetscCall(PetscOptionsBool("-dm_plex_hash_location", "Use grid hashing for point location", "DMInterpolate", PETSC_FALSE, &mesh->useHashLocation, NULL));
5098   /* Partitioning and distribution */
5099   PetscCall(PetscOptionsBool("-dm_plex_partition_balance", "Attempt to evenly divide points on partition boundary between processes", "DMPlexSetPartitionBalance", PETSC_FALSE, &mesh->partitionBalance, NULL));
5100   /* Reordering */
5101   PetscCall(PetscOptionsBool("-dm_reorder_section", "Compute point permutation for local section", "DMReorderSectionSetDefault", PETSC_FALSE, &flg2, &flg));
5102   if (flg) PetscCall(DMReorderSectionSetDefault(dm, flg2 ? DM_REORDER_DEFAULT_TRUE : DM_REORDER_DEFAULT_FALSE));
5103   PetscCall(PetscOptionsString("-dm_reorder_section_type", "Reordering method for local section", "DMReorderSectionSetType", method, method, PETSC_MAX_PATH_LEN, &flg));
5104   if (flg) PetscCall(DMReorderSectionSetType(dm, method));
5105   /* Generation and remeshing */
5106   PetscCall(PetscOptionsBool("-dm_plex_remesh_bd", "Allow changes to the boundary on remeshing", "DMAdapt", PETSC_FALSE, &mesh->remeshBd, NULL));
5107   PetscCall(PetscOptionsBool("-dm_plex_save_transform", "Save the transform producing this mesh", "DMAdapt", PETSC_FALSE, &mesh->saveTransform, NULL));
5108   /* Projection behavior */
5109   PetscCall(PetscOptionsBoundedInt("-dm_plex_max_projection_height", "Maximum mesh point height used to project locally", "DMPlexSetMaxProjectionHeight", 0, &mesh->maxProjectionHeight, NULL, 0));
5110   PetscCall(PetscOptionsBool("-dm_plex_regular_refinement", "Use special nested projection algorithm for regular refinement", "DMPlexSetRegularRefinement", mesh->regularRefinement, &mesh->regularRefinement, NULL));
5111   /* Checking structure */
5112   {
5113     PetscBool all = PETSC_FALSE;
5114 
5115     PetscCall(PetscOptionsBool("-dm_plex_check_all", "Perform all basic checks", "DMPlexCheck", PETSC_FALSE, &all, NULL));
5116     if (all) {
5117       PetscCall(DMPlexCheck(dm));
5118     } else {
5119       PetscCall(PetscOptionsBool("-dm_plex_check_symmetry", "Check that the adjacency information in the mesh is symmetric", "DMPlexCheckSymmetry", PETSC_FALSE, &flg, &flg2));
5120       if (flg && flg2) PetscCall(DMPlexCheckSymmetry(dm));
5121       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));
5122       if (flg && flg2) PetscCall(DMPlexCheckSkeleton(dm, 0));
5123       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));
5124       if (flg && flg2) PetscCall(DMPlexCheckFaces(dm, 0));
5125       PetscCall(PetscOptionsBool("-dm_plex_check_geometry", "Check that cells have positive volume", "DMPlexCheckGeometry", PETSC_FALSE, &flg, &flg2));
5126       if (flg && flg2) PetscCall(DMPlexCheckGeometry(dm));
5127       PetscCall(PetscOptionsBool("-dm_plex_check_pointsf", "Check some necessary conditions for PointSF", "DMPlexCheckPointSF", PETSC_FALSE, &flg, &flg2));
5128       if (flg && flg2) PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
5129       PetscCall(PetscOptionsBool("-dm_plex_check_interface_cones", "Check points on inter-partition interfaces have conforming order of cone points", "DMPlexCheckInterfaceCones", PETSC_FALSE, &flg, &flg2));
5130       if (flg && flg2) PetscCall(DMPlexCheckInterfaceCones(dm));
5131     }
5132     PetscCall(PetscOptionsBool("-dm_plex_check_cell_shape", "Check cell shape", "DMPlexCheckCellShape", PETSC_FALSE, &flg, &flg2));
5133     if (flg && flg2) PetscCall(DMPlexCheckCellShape(dm, PETSC_TRUE, PETSC_DETERMINE));
5134   }
5135   {
5136     PetscReal scale = 1.0;
5137 
5138     PetscCall(PetscOptionsReal("-dm_plex_scale", "Scale factor for mesh coordinates", "DMPlexScale", scale, &scale, &flg));
5139     if (flg) {
5140       Vec coordinates, coordinatesLocal;
5141 
5142       PetscCall(DMGetCoordinates(dm, &coordinates));
5143       PetscCall(DMGetCoordinatesLocal(dm, &coordinatesLocal));
5144       PetscCall(VecScale(coordinates, scale));
5145       PetscCall(VecScale(coordinatesLocal, scale));
5146     }
5147   }
5148   PetscCall(PetscPartitionerSetFromOptions(mesh->partitioner));
5149   PetscFunctionReturn(PETSC_SUCCESS);
5150 }
5151 
5152 PetscErrorCode DMSetFromOptions_Overlap_Plex(DM dm, PetscOptionItems PetscOptionsObject, PetscInt *overlap)
5153 {
5154   PetscInt  numOvLabels = 16, numOvExLabels = 16;
5155   char     *ovLabelNames[16], *ovExLabelNames[16];
5156   PetscInt  numOvValues = 16, numOvExValues = 16, l;
5157   PetscBool flg;
5158 
5159   PetscFunctionBegin;
5160   PetscCall(PetscOptionsBoundedInt("-dm_distribute_overlap", "The size of the overlap halo", "DMPlexDistribute", *overlap, overlap, NULL, 0));
5161   PetscCall(PetscOptionsStringArray("-dm_distribute_overlap_labels", "List of overlap label names", "DMPlexDistribute", ovLabelNames, &numOvLabels, &flg));
5162   if (!flg) numOvLabels = 0;
5163   if (numOvLabels) {
5164     ((DM_Plex *)dm->data)->numOvLabels = numOvLabels;
5165     for (l = 0; l < numOvLabels; ++l) {
5166       PetscCall(DMGetLabel(dm, ovLabelNames[l], &((DM_Plex *)dm->data)->ovLabels[l]));
5167       PetscCheck(((DM_Plex *)dm->data)->ovLabels[l], PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Invalid label name %s", ovLabelNames[l]);
5168       PetscCall(PetscFree(ovLabelNames[l]));
5169     }
5170     PetscCall(PetscOptionsIntArray("-dm_distribute_overlap_values", "List of overlap label values", "DMPlexDistribute", ((DM_Plex *)dm->data)->ovValues, &numOvValues, &flg));
5171     if (!flg) numOvValues = 0;
5172     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);
5173 
5174     PetscCall(PetscOptionsStringArray("-dm_distribute_overlap_exclude_labels", "List of overlap exclude label names", "DMPlexDistribute", ovExLabelNames, &numOvExLabels, &flg));
5175     if (!flg) numOvExLabels = 0;
5176     ((DM_Plex *)dm->data)->numOvExLabels = numOvExLabels;
5177     for (l = 0; l < numOvExLabels; ++l) {
5178       PetscCall(DMGetLabel(dm, ovExLabelNames[l], &((DM_Plex *)dm->data)->ovExLabels[l]));
5179       PetscCheck(((DM_Plex *)dm->data)->ovExLabels[l], PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Invalid label name %s", ovExLabelNames[l]);
5180       PetscCall(PetscFree(ovExLabelNames[l]));
5181     }
5182     PetscCall(PetscOptionsIntArray("-dm_distribute_overlap_exclude_values", "List of overlap exclude label values", "DMPlexDistribute", ((DM_Plex *)dm->data)->ovExValues, &numOvExValues, &flg));
5183     if (!flg) numOvExValues = 0;
5184     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);
5185   }
5186   PetscFunctionReturn(PETSC_SUCCESS);
5187 }
5188 
5189 static PetscErrorCode DMSetFromOptions_Plex(DM dm, PetscOptionItems PetscOptionsObject)
5190 {
5191   PetscFunctionList    ordlist;
5192   char                 oname[256];
5193   char                 sublabelname[PETSC_MAX_PATH_LEN] = "";
5194   DMReorderDefaultFlag reorder;
5195   PetscReal            volume    = -1.0;
5196   PetscInt             prerefine = 0, refine = 0, r, coarsen = 0, overlap = 0, extLayers = 0, dim;
5197   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;
5198 
5199   PetscFunctionBegin;
5200   PetscOptionsHeadBegin(PetscOptionsObject, "DMPlex Options");
5201   if (dm->cloneOpts) goto non_refine;
5202   /* Handle automatic creation */
5203   PetscCall(DMGetDimension(dm, &dim));
5204   if (dim < 0) PetscCall(DMPlexCreateFromOptions_Internal(PetscOptionsObject, &coordSpace, dm));
5205   PetscCall(DMGetDimension(dm, &dim));
5206   /* Handle interpolation before distribution */
5207   PetscCall(PetscOptionsBool("-dm_plex_interpolate_pre", "Flag to interpolate mesh before distribution", "", interpolate, &interpolate, &flg));
5208   if (flg) {
5209     DMPlexInterpolatedFlag interpolated;
5210 
5211     PetscCall(DMPlexIsInterpolated(dm, &interpolated));
5212     if (interpolated == DMPLEX_INTERPOLATED_FULL && !interpolate) {
5213       DM udm;
5214 
5215       PetscCall(DMPlexUninterpolate(dm, &udm));
5216       PetscCall(DMPlexReplace_Internal(dm, &udm));
5217     } else if (interpolated != DMPLEX_INTERPOLATED_FULL && interpolate) {
5218       DM idm;
5219 
5220       PetscCall(DMPlexInterpolate(dm, &idm));
5221       PetscCall(DMPlexReplace_Internal(dm, &idm));
5222     }
5223   }
5224   // Handle submesh selection before distribution
5225   PetscCall(PetscOptionsString("-dm_plex_submesh", "Label to use for submesh selection", "", sublabelname, sublabelname, PETSC_MAX_PATH_LEN, &flg));
5226   if (flg) {
5227     DM              subdm;
5228     DMLabel         label;
5229     IS              valueIS, pointIS;
5230     const PetscInt *values, *points;
5231     PetscBool       markedFaces = PETSC_FALSE;
5232     PetscInt        Nv, value, Np;
5233 
5234     PetscCall(DMGetLabel(dm, sublabelname, &label));
5235     PetscCall(DMLabelGetNumValues(label, &Nv));
5236     PetscCheck(Nv == 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Only a single label value is currently supported for submesh selection, not %" PetscInt_FMT, Nv);
5237     PetscCall(DMLabelGetValueIS(label, &valueIS));
5238     PetscCall(ISGetIndices(valueIS, &values));
5239     value = values[0];
5240     PetscCall(ISRestoreIndices(valueIS, &values));
5241     PetscCall(ISDestroy(&valueIS));
5242     PetscCall(DMLabelGetStratumSize(label, value, &Np));
5243     PetscCall(DMLabelGetStratumIS(label, value, &pointIS));
5244     PetscCall(ISGetIndices(pointIS, &points));
5245     for (PetscInt p = 0; p < Np; ++p) {
5246       PetscInt pdepth;
5247 
5248       PetscCall(DMPlexGetPointDepth(dm, points[p], &pdepth));
5249       if (pdepth) {
5250         markedFaces = PETSC_TRUE;
5251         break;
5252       }
5253     }
5254     PetscCall(ISRestoreIndices(pointIS, &points));
5255     PetscCall(ISDestroy(&pointIS));
5256     PetscCall(DMPlexCreateSubmesh(dm, label, value, markedFaces, &subdm));
5257     PetscCall(DMPlexReplace_Internal(dm, &subdm));
5258     PetscCall(DMSetFromOptions_NonRefinement_Plex(dm, PetscOptionsObject));
5259   }
5260   /* Handle DMPlex refinement before distribution */
5261   PetscCall(DMPlexGetRefinementUniform(dm, &uniformOrig));
5262   PetscCall(PetscOptionsBoundedInt("-dm_refine_pre", "The number of refinements before distribution", "DMCreate", prerefine, &prerefine, NULL, 0));
5263   PetscCall(PetscOptionsBool("-dm_refine_remap_pre", "Flag to control coordinate remapping", "DMCreate", remap, &remap, NULL));
5264   PetscCall(PetscOptionsBool("-dm_refine_uniform_pre", "Flag for uniform refinement before distribution", "DMCreate", uniform, &uniform, &flg));
5265   if (flg) PetscCall(DMPlexSetRefinementUniform(dm, uniform));
5266   PetscCall(PetscOptionsReal("-dm_refine_volume_limit_pre", "The maximum cell volume after refinement before distribution", "DMCreate", volume, &volume, &flg));
5267   if (flg) {
5268     PetscCall(DMPlexSetRefinementUniform(dm, PETSC_FALSE));
5269     PetscCall(DMPlexSetRefinementLimit(dm, volume));
5270     prerefine = PetscMax(prerefine, 1);
5271   }
5272   if (prerefine) PetscCall(DMLocalizeCoordinates(dm));
5273   for (r = 0; r < prerefine; ++r) {
5274     DM            rdm;
5275     PetscPointFn *coordFunc;
5276 
5277     PetscCall(DMPlexGetCoordinateMap(dm, &coordFunc));
5278     PetscCall(DMSetFromOptions_NonRefinement_Plex(dm, PetscOptionsObject));
5279     PetscCall(DMRefine(dm, PetscObjectComm((PetscObject)dm), &rdm));
5280     PetscCall(DMPlexReplace_Internal(dm, &rdm));
5281     PetscCall(DMSetFromOptions_NonRefinement_Plex(dm, PetscOptionsObject));
5282     if (coordFunc && remap) {
5283       PetscCall(DMPlexRemapGeometry(dm, 0.0, coordFunc));
5284       PetscCall(DMPlexSetCoordinateMap(dm, coordFunc));
5285     }
5286   }
5287   PetscCall(DMPlexSetRefinementUniform(dm, uniformOrig));
5288   /* Handle DMPlex extrusion before distribution */
5289   PetscCall(PetscOptionsBoundedInt("-dm_extrude", "The number of layers to extrude", "", extLayers, &extLayers, NULL, 0));
5290   if (extLayers) {
5291     DM edm;
5292 
5293     PetscCall(DMExtrude(dm, extLayers, &edm));
5294     PetscCall(DMPlexReplace_Internal(dm, &edm));
5295     PetscCall(DMPlexSetCoordinateMap(dm, NULL));
5296     PetscCall(DMSetFromOptions_NonRefinement_Plex(dm, PetscOptionsObject));
5297     extLayers = 0;
5298     PetscCall(DMGetDimension(dm, &dim));
5299   }
5300   /* Handle DMPlex reordering before distribution */
5301   PetscCall(DMPlexReorderGetDefault(dm, &reorder));
5302   PetscCall(MatGetOrderingList(&ordlist));
5303   PetscCall(PetscStrncpy(oname, MATORDERINGNATURAL, sizeof(oname)));
5304   PetscCall(PetscOptionsFList("-dm_plex_reorder", "Set mesh reordering type", "DMPlexGetOrdering", ordlist, MATORDERINGNATURAL, oname, sizeof(oname), &flg));
5305   if (reorder == DM_REORDER_DEFAULT_TRUE || flg) {
5306     DM pdm;
5307     IS perm;
5308 
5309     PetscCall(DMPlexGetOrdering(dm, oname, NULL, &perm));
5310     PetscCall(DMPlexPermute(dm, perm, &pdm));
5311     PetscCall(ISDestroy(&perm));
5312     PetscCall(DMPlexReplace_Internal(dm, &pdm));
5313     PetscCall(DMSetFromOptions_NonRefinement_Plex(dm, PetscOptionsObject));
5314   }
5315   /* Handle DMPlex distribution */
5316   PetscCall(DMPlexDistributeGetDefault(dm, &distribute));
5317   PetscCall(PetscOptionsBool("-dm_distribute", "Flag to redistribute a mesh among processes", "DMPlexDistribute", distribute, &distribute, NULL));
5318   PetscCall(PetscOptionsBool("-dm_distribute_save_sf", "Flag to save the migration SF", "DMPlexSetMigrationSF", saveSF, &saveSF, NULL));
5319   PetscCall(DMSetFromOptions_Overlap_Plex(dm, PetscOptionsObject, &overlap));
5320   if (distribute) {
5321     DM               pdm = NULL;
5322     PetscPartitioner part;
5323     PetscSF          sfMigration;
5324     PetscBool        use_partition_balance;
5325 
5326     PetscCall(DMPlexGetPartitioner(dm, &part));
5327     PetscCall(PetscPartitionerSetFromOptions(part));
5328     PetscCall(DMPlexGetPartitionBalance(dm, &use_partition_balance));
5329     PetscCall(PetscOptionsBool("-dm_plex_partition_balance", "Attempt to evenly divide points on partition boundary between processes", "DMPlexSetPartitionBalance", use_partition_balance, &use_partition_balance, &flg));
5330     if (flg) PetscCall(DMPlexSetPartitionBalance(dm, use_partition_balance));
5331     PetscCall(DMPlexDistribute(dm, overlap, &sfMigration, &pdm));
5332     if (pdm) {
5333       // Delete the local section to force the existing one to be rebuilt with the distributed DM
5334       PetscCall(DMSetLocalSection(dm, pdm->localSection));
5335       PetscCall(DMPlexReplace_Internal(dm, &pdm));
5336     }
5337     if (saveSF) PetscCall(DMPlexSetMigrationSF(dm, sfMigration));
5338     PetscCall(PetscSFDestroy(&sfMigration));
5339   }
5340 
5341   {
5342     PetscBool useBoxLabel = PETSC_FALSE;
5343     PetscCall(PetscOptionsBool("-dm_plex_box_label", "Create 'Face Sets' assuming boundary faces align with cartesian directions", "DMCreate", useBoxLabel, &useBoxLabel, NULL));
5344     if (useBoxLabel) {
5345       PetscInt       n      = 3;
5346       DMBoundaryType bdt[3] = {DM_BOUNDARY_NONE, DM_BOUNDARY_NONE, DM_BOUNDARY_NONE};
5347 
5348       PetscCall(PetscOptionsEnumArray("-dm_plex_box_label_bd", "Boundary type for each dimension when using -dm_plex_box_label", "", DMBoundaryTypes, (PetscEnum *)bdt, &n, &flg));
5349       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);
5350       PetscCall(DMPlexSetBoxLabel_Internal(dm, bdt));
5351     }
5352   }
5353   /* Must check CEED options before creating function space for coordinates */
5354   {
5355     PetscBool useCeed = PETSC_FALSE, flg;
5356 
5357     PetscCall(PetscOptionsBool("-dm_plex_use_ceed", "Use LibCEED as the FEM backend", "DMPlexSetUseCeed", useCeed, &useCeed, &flg));
5358     if (flg) PetscCall(DMPlexSetUseCeed(dm, useCeed));
5359   }
5360   /* Create coordinate space */
5361   PetscCall(PetscOptionsBool("-dm_coord_space", "Use an FEM space for coordinates", "", coordSpace, &coordSpace, NULL));
5362   if (coordSpace) {
5363     PetscInt  degree = 1, deg;
5364     PetscInt  height = 0;
5365     DM        cdm;
5366     PetscBool localize = PETSC_TRUE, sparseLocalize = PETSC_TRUE;
5367 
5368     PetscCall(PetscOptionsInt("-dm_coord_petscspace_degree", "FEM degree for coordinate space", "", degree, &degree, NULL));
5369     PetscCall(DMGetCoordinateDegree_Internal(dm, &deg));
5370     if (coordSpace && deg <= 1) PetscCall(DMPlexCreateCoordinateSpace(dm, degree, PETSC_FALSE, PETSC_TRUE));
5371     PetscCall(DMGetCoordinateDM(dm, &cdm));
5372     if (!coordSpace) {
5373       PetscDS      cds;
5374       PetscObject  obj;
5375       PetscClassId id;
5376 
5377       PetscCall(DMGetDS(cdm, &cds));
5378       PetscCall(PetscDSGetDiscretization(cds, 0, &obj));
5379       PetscCall(PetscObjectGetClassId(obj, &id));
5380       if (id == PETSCFE_CLASSID) {
5381         PetscContainer dummy;
5382 
5383         PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &dummy));
5384         PetscCall(PetscObjectSetName((PetscObject)dummy, "coordinates"));
5385         PetscCall(DMSetField(cdm, 0, NULL, (PetscObject)dummy));
5386         PetscCall(PetscContainerDestroy(&dummy));
5387         PetscCall(DMClearDS(cdm));
5388       }
5389       PetscCall(DMPlexSetCoordinateMap(dm, NULL));
5390     }
5391     PetscCall(PetscOptionsBool("-dm_localize", "Localize mesh coordinates", "", localize, &localize, NULL));
5392     PetscCall(PetscOptionsBool("-dm_sparse_localize", "Localize only necessary cells", "DMSetSparseLocalize", sparseLocalize, &sparseLocalize, &flg));
5393     if (flg) PetscCall(DMSetSparseLocalize(dm, sparseLocalize));
5394     PetscCall(PetscOptionsInt("-dm_localize_height", "Localize edges and faces in addition to cells", "", height, &height, &flg));
5395     if (flg) PetscCall(DMPlexSetMaxProjectionHeight(cdm, height));
5396     if (localize) PetscCall(DMLocalizeCoordinates(dm));
5397   }
5398   /* Handle DMPlex refinement */
5399   remap = PETSC_TRUE;
5400   PetscCall(PetscOptionsBoundedInt("-dm_refine", "The number of uniform refinements", "DMCreate", refine, &refine, NULL, 0));
5401   PetscCall(PetscOptionsBool("-dm_refine_remap", "Flag to control coordinate remapping", "DMCreate", remap, &remap, NULL));
5402   PetscCall(PetscOptionsBoundedInt("-dm_refine_hierarchy", "The number of uniform refinements", "DMCreate", refine, &refine, &isHierarchy, 0));
5403   if (refine) PetscCall(DMPlexSetRefinementUniform(dm, PETSC_TRUE));
5404   if (refine && isHierarchy) {
5405     DM *dms, coarseDM;
5406 
5407     PetscCall(DMGetCoarseDM(dm, &coarseDM));
5408     PetscCall(PetscObjectReference((PetscObject)coarseDM));
5409     PetscCall(PetscMalloc1(refine, &dms));
5410     PetscCall(DMRefineHierarchy(dm, refine, dms));
5411     /* Total hack since we do not pass in a pointer */
5412     PetscCall(DMPlexSwap_Static(dm, dms[refine - 1]));
5413     if (refine == 1) {
5414       PetscCall(DMSetCoarseDM(dm, dms[0]));
5415       PetscCall(DMPlexSetRegularRefinement(dm, PETSC_TRUE));
5416     } else {
5417       PetscCall(DMSetCoarseDM(dm, dms[refine - 2]));
5418       PetscCall(DMPlexSetRegularRefinement(dm, PETSC_TRUE));
5419       PetscCall(DMSetCoarseDM(dms[0], dms[refine - 1]));
5420       PetscCall(DMPlexSetRegularRefinement(dms[0], PETSC_TRUE));
5421     }
5422     PetscCall(DMSetCoarseDM(dms[refine - 1], coarseDM));
5423     PetscCall(PetscObjectDereference((PetscObject)coarseDM));
5424     /* Free DMs */
5425     for (r = 0; r < refine; ++r) {
5426       PetscCall(DMSetFromOptions_NonRefinement_Plex(dms[r], PetscOptionsObject));
5427       PetscCall(DMDestroy(&dms[r]));
5428     }
5429     PetscCall(PetscFree(dms));
5430   } else {
5431     for (r = 0; r < refine; ++r) {
5432       DM            rdm;
5433       PetscPointFn *coordFunc;
5434 
5435       PetscCall(DMSetFromOptions_NonRefinement_Plex(dm, PetscOptionsObject));
5436       PetscCall(DMRefine(dm, PetscObjectComm((PetscObject)dm), &rdm));
5437       /* Total hack since we do not pass in a pointer */
5438       PetscCall(DMPlexReplace_Internal(dm, &rdm));
5439       PetscCall(DMSetFromOptions_NonRefinement_Plex(dm, PetscOptionsObject));
5440       PetscCall(DMPlexGetCoordinateMap(dm, &coordFunc));
5441       if (coordFunc && remap) {
5442         PetscCall(DMPlexRemapGeometry(dm, 0.0, coordFunc));
5443         PetscCall(DMPlexSetCoordinateMap(dm, coordFunc));
5444       }
5445     }
5446   }
5447   /* Handle DMPlex coarsening */
5448   PetscCall(PetscOptionsBoundedInt("-dm_coarsen", "Coarsen the mesh", "DMCreate", coarsen, &coarsen, NULL, 0));
5449   PetscCall(PetscOptionsBoundedInt("-dm_coarsen_hierarchy", "The number of coarsenings", "DMCreate", coarsen, &coarsen, &isHierarchy, 0));
5450   if (coarsen && isHierarchy) {
5451     DM *dms;
5452 
5453     PetscCall(PetscMalloc1(coarsen, &dms));
5454     PetscCall(DMCoarsenHierarchy(dm, coarsen, dms));
5455     /* Free DMs */
5456     for (r = 0; r < coarsen; ++r) {
5457       PetscCall(DMSetFromOptions_NonRefinement_Plex(dms[r], PetscOptionsObject));
5458       PetscCall(DMDestroy(&dms[r]));
5459     }
5460     PetscCall(PetscFree(dms));
5461   } else {
5462     for (r = 0; r < coarsen; ++r) {
5463       DM            cdm;
5464       PetscPointFn *coordFunc;
5465 
5466       PetscCall(DMPlexGetCoordinateMap(dm, &coordFunc));
5467       PetscCall(DMSetFromOptions_NonRefinement_Plex(dm, PetscOptionsObject));
5468       PetscCall(DMCoarsen(dm, PetscObjectComm((PetscObject)dm), &cdm));
5469       /* Total hack since we do not pass in a pointer */
5470       PetscCall(DMPlexReplace_Internal(dm, &cdm));
5471       PetscCall(DMSetFromOptions_NonRefinement_Plex(dm, PetscOptionsObject));
5472       if (coordFunc) {
5473         PetscCall(DMPlexRemapGeometry(dm, 0.0, coordFunc));
5474         PetscCall(DMPlexSetCoordinateMap(dm, coordFunc));
5475       }
5476     }
5477   }
5478   // Handle coordinate remapping
5479   remap = PETSC_FALSE;
5480   PetscCall(PetscOptionsBool("-dm_coord_remap", "Flag to control coordinate remapping", "", remap, &remap, NULL));
5481   if (remap) {
5482     DMPlexCoordMap map     = DM_COORD_MAP_NONE;
5483     PetscPointFn  *mapFunc = NULL;
5484     PetscScalar    params[16];
5485     PetscInt       Np = PETSC_STATIC_ARRAY_LENGTH(params), cdim;
5486     MPI_Comm       comm;
5487 
5488     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
5489     PetscCall(DMGetCoordinateDim(dm, &cdim));
5490     PetscCall(PetscOptionsScalarArray("-dm_coord_map_params", "Parameters for the coordinate remapping", "", params, &Np, &flg));
5491     if (!flg) Np = 0;
5492     // TODO Allow user to pass a map function by name
5493     PetscCall(PetscOptionsEnum("-dm_coord_map", "Coordinate mapping for built-in mesh", "", DMPlexCoordMaps, (PetscEnum)map, (PetscEnum *)&map, &flg));
5494     if (flg) {
5495       switch (map) {
5496       case DM_COORD_MAP_NONE:
5497         mapFunc = coordMap_identity;
5498         break;
5499       case DM_COORD_MAP_SHEAR:
5500         mapFunc = coordMap_shear;
5501         if (!Np) {
5502           Np        = cdim + 1;
5503           params[0] = 0;
5504           for (PetscInt d = 1; d <= cdim; ++d) params[d] = 1.0;
5505         }
5506         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);
5507         break;
5508       case DM_COORD_MAP_FLARE:
5509         mapFunc = coordMap_flare;
5510         if (!Np) {
5511           Np        = cdim + 1;
5512           params[0] = 0;
5513           for (PetscInt d = 1; d <= cdim; ++d) params[d] = 1.0;
5514         }
5515         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);
5516         break;
5517       case DM_COORD_MAP_ANNULUS:
5518         mapFunc = coordMap_annulus;
5519         if (!Np) {
5520           Np        = 2;
5521           params[0] = 1.;
5522           params[1] = 2.;
5523         }
5524         PetscCheck(Np == 2, comm, PETSC_ERR_ARG_WRONG, "The annulus coordinate map must have 2 parameters, not %" PetscInt_FMT, Np);
5525         break;
5526       case DM_COORD_MAP_SHELL:
5527         mapFunc = coordMap_shell;
5528         if (!Np) {
5529           Np        = 2;
5530           params[0] = 1.;
5531           params[1] = 2.;
5532         }
5533         PetscCheck(Np == 2, comm, PETSC_ERR_ARG_WRONG, "The spherical shell coordinate map must have 2 parameters, not %" PetscInt_FMT, Np);
5534         break;
5535       case DM_COORD_MAP_SINUSOID:
5536         mapFunc = coordMap_sinusoid;
5537         if (!Np) {
5538           Np        = 3;
5539           params[0] = 1.;
5540           params[1] = 1.;
5541           params[2] = 1.;
5542         }
5543         PetscCheck(Np == 3, comm, PETSC_ERR_ARG_WRONG, "The sinusoidal coordinate map must have 3 parameters, not %" PetscInt_FMT, Np);
5544         break;
5545       default:
5546         mapFunc = coordMap_identity;
5547       }
5548     }
5549     if (Np) {
5550       DM      cdm;
5551       PetscDS cds;
5552 
5553       PetscCall(DMGetCoordinateDM(dm, &cdm));
5554       PetscCall(DMGetDS(cdm, &cds));
5555       PetscCall(PetscDSSetConstants(cds, Np, params));
5556     }
5557     PetscCall(DMPlexRemapGeometry(dm, 0.0, mapFunc));
5558   }
5559   /* Handle ghost cells */
5560   PetscCall(PetscOptionsBool("-dm_plex_create_fv_ghost_cells", "Flag to create finite volume ghost cells on the boundary", "DMCreate", ghostCells, &ghostCells, NULL));
5561   if (ghostCells) {
5562     DM   gdm;
5563     char lname[PETSC_MAX_PATH_LEN];
5564 
5565     lname[0] = '\0';
5566     PetscCall(PetscOptionsString("-dm_plex_fv_ghost_cells_label", "Label name for ghost cells boundary", "DMCreate", lname, lname, sizeof(lname), &flg));
5567     PetscCall(DMPlexConstructGhostCells(dm, flg ? lname : NULL, NULL, &gdm));
5568     PetscCall(DMPlexReplace_Internal(dm, &gdm));
5569   }
5570   /* Handle 1D order */
5571   if (reorder != DM_REORDER_DEFAULT_FALSE && dim == 1) {
5572     DM           cdm, rdm;
5573     PetscDS      cds;
5574     PetscObject  obj;
5575     PetscClassId id = PETSC_OBJECT_CLASSID;
5576     IS           perm;
5577     PetscInt     Nf;
5578     PetscBool    distributed;
5579 
5580     PetscCall(DMPlexIsDistributed(dm, &distributed));
5581     PetscCall(DMGetCoordinateDM(dm, &cdm));
5582     PetscCall(DMGetDS(cdm, &cds));
5583     PetscCall(PetscDSGetNumFields(cds, &Nf));
5584     if (Nf) {
5585       PetscCall(PetscDSGetDiscretization(cds, 0, &obj));
5586       PetscCall(PetscObjectGetClassId(obj, &id));
5587     }
5588     if (!distributed && id != PETSCFE_CLASSID) {
5589       PetscCall(DMPlexGetOrdering1D(dm, &perm));
5590       PetscCall(DMPlexPermute(dm, perm, &rdm));
5591       PetscCall(DMPlexReplace_Internal(dm, &rdm));
5592       PetscCall(ISDestroy(&perm));
5593     }
5594   }
5595 /* Handle */
5596 non_refine:
5597   PetscCall(DMSetFromOptions_NonRefinement_Plex(dm, PetscOptionsObject));
5598   char    *phases[16];
5599   PetscInt Nphases = 16;
5600   PetscCall(PetscOptionsStringArray("-dm_plex_option_phases", "Option phase prefixes", "DMSetFromOptions", phases, &Nphases, &flg));
5601   PetscOptionsHeadEnd();
5602 
5603   // Phases
5604   if (flg) {
5605     DM          cdm;
5606     char       *oldPrefix, *oldCoordPrefix;
5607     const char *tmp;
5608 
5609     PetscCall(DMGetCoordinateDM(dm, &cdm));
5610     PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &tmp));
5611     PetscCall(PetscStrallocpy(tmp, &oldPrefix));
5612     PetscCall(PetscObjectGetOptionsPrefix((PetscObject)cdm, &tmp));
5613     PetscCall(PetscStrallocpy(tmp, &oldCoordPrefix));
5614     for (PetscInt ph = 0; ph < Nphases; ++ph) {
5615       PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)dm, phases[ph]));
5616       PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)cdm, phases[ph]));
5617       PetscCall(PetscInfo(dm, "Options phase %s for DM %s\n", phases[ph], dm->hdr.name));
5618       PetscCall(DMSetFromOptions(dm));
5619       PetscCall(PetscObjectSetOptionsPrefix((PetscObject)dm, oldPrefix));
5620       PetscCall(DMGetCoordinateDM(dm, &cdm));
5621       PetscCall(PetscObjectSetOptionsPrefix((PetscObject)cdm, oldCoordPrefix));
5622       PetscCall(PetscFree(phases[ph]));
5623     }
5624     PetscCall(PetscFree(oldPrefix));
5625     PetscCall(PetscFree(oldCoordPrefix));
5626   }
5627   PetscFunctionReturn(PETSC_SUCCESS);
5628 }
5629 
5630 static PetscErrorCode DMCreateGlobalVector_Plex(DM dm, Vec *vec)
5631 {
5632   PetscFunctionBegin;
5633   PetscCall(DMCreateGlobalVector_Section_Private(dm, vec));
5634   /* PetscCall(VecSetOperation(*vec, VECOP_DUPLICATE, (void(*)(void)) VecDuplicate_MPI_DM)); */
5635   PetscCall(VecSetOperation(*vec, VECOP_VIEW, (void (*)(void))VecView_Plex));
5636   PetscCall(VecSetOperation(*vec, VECOP_VIEWNATIVE, (void (*)(void))VecView_Plex_Native));
5637   PetscCall(VecSetOperation(*vec, VECOP_LOAD, (void (*)(void))VecLoad_Plex));
5638   PetscCall(VecSetOperation(*vec, VECOP_LOADNATIVE, (void (*)(void))VecLoad_Plex_Native));
5639   PetscFunctionReturn(PETSC_SUCCESS);
5640 }
5641 
5642 static PetscErrorCode DMCreateLocalVector_Plex(DM dm, Vec *vec)
5643 {
5644   PetscFunctionBegin;
5645   PetscCall(DMCreateLocalVector_Section_Private(dm, vec));
5646   PetscCall(VecSetOperation(*vec, VECOP_VIEW, (void (*)(void))VecView_Plex_Local));
5647   PetscCall(VecSetOperation(*vec, VECOP_LOAD, (void (*)(void))VecLoad_Plex_Local));
5648   PetscFunctionReturn(PETSC_SUCCESS);
5649 }
5650 
5651 static PetscErrorCode DMGetDimPoints_Plex(DM dm, PetscInt dim, PetscInt *pStart, PetscInt *pEnd)
5652 {
5653   PetscInt depth, d;
5654 
5655   PetscFunctionBegin;
5656   PetscCall(DMPlexGetDepth(dm, &depth));
5657   if (depth == 1) {
5658     PetscCall(DMGetDimension(dm, &d));
5659     if (dim == 0) PetscCall(DMPlexGetDepthStratum(dm, dim, pStart, pEnd));
5660     else if (dim == d) PetscCall(DMPlexGetDepthStratum(dm, 1, pStart, pEnd));
5661     else {
5662       *pStart = 0;
5663       *pEnd   = 0;
5664     }
5665   } else {
5666     PetscCall(DMPlexGetDepthStratum(dm, dim, pStart, pEnd));
5667   }
5668   PetscFunctionReturn(PETSC_SUCCESS);
5669 }
5670 
5671 static PetscErrorCode DMGetNeighbors_Plex(DM dm, PetscInt *nranks, const PetscMPIInt *ranks[])
5672 {
5673   PetscSF            sf;
5674   PetscMPIInt        niranks, njranks;
5675   PetscInt           n;
5676   const PetscMPIInt *iranks, *jranks;
5677   DM_Plex           *data = (DM_Plex *)dm->data;
5678 
5679   PetscFunctionBegin;
5680   PetscCall(DMGetPointSF(dm, &sf));
5681   if (!data->neighbors) {
5682     PetscCall(PetscSFSetUp(sf));
5683     PetscCall(PetscSFGetRootRanks(sf, &njranks, &jranks, NULL, NULL, NULL));
5684     PetscCall(PetscSFGetLeafRanks(sf, &niranks, &iranks, NULL, NULL));
5685     PetscCall(PetscMalloc1(njranks + niranks + 1, &data->neighbors));
5686     PetscCall(PetscArraycpy(data->neighbors + 1, jranks, njranks));
5687     PetscCall(PetscArraycpy(data->neighbors + njranks + 1, iranks, niranks));
5688     n = njranks + niranks;
5689     PetscCall(PetscSortRemoveDupsMPIInt(&n, data->neighbors + 1));
5690     /* The following cast should never fail: can't have more neighbors than PETSC_MPI_INT_MAX */
5691     PetscCall(PetscMPIIntCast(n, data->neighbors));
5692   }
5693   if (nranks) *nranks = data->neighbors[0];
5694   if (ranks) {
5695     if (data->neighbors[0]) *ranks = data->neighbors + 1;
5696     else *ranks = NULL;
5697   }
5698   PetscFunctionReturn(PETSC_SUCCESS);
5699 }
5700 
5701 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM, DM, Mat, Vec, Vec);
5702 
5703 static PetscErrorCode DMInitialize_Plex(DM dm)
5704 {
5705   PetscFunctionBegin;
5706   dm->ops->view                      = DMView_Plex;
5707   dm->ops->load                      = DMLoad_Plex;
5708   dm->ops->setfromoptions            = DMSetFromOptions_Plex;
5709   dm->ops->clone                     = DMClone_Plex;
5710   dm->ops->setup                     = DMSetUp_Plex;
5711   dm->ops->createlocalsection        = DMCreateLocalSection_Plex;
5712   dm->ops->createsectionpermutation  = DMCreateSectionPermutation_Plex;
5713   dm->ops->createdefaultconstraints  = DMCreateDefaultConstraints_Plex;
5714   dm->ops->createglobalvector        = DMCreateGlobalVector_Plex;
5715   dm->ops->createlocalvector         = DMCreateLocalVector_Plex;
5716   dm->ops->getlocaltoglobalmapping   = NULL;
5717   dm->ops->createfieldis             = NULL;
5718   dm->ops->createcoordinatedm        = DMCreateCoordinateDM_Plex;
5719   dm->ops->createcellcoordinatedm    = DMCreateCellCoordinateDM_Plex;
5720   dm->ops->createcoordinatefield     = DMCreateCoordinateField_Plex;
5721   dm->ops->getcoloring               = NULL;
5722   dm->ops->creatematrix              = DMCreateMatrix_Plex;
5723   dm->ops->createinterpolation       = DMCreateInterpolation_Plex;
5724   dm->ops->createmassmatrix          = DMCreateMassMatrix_Plex;
5725   dm->ops->createmassmatrixlumped    = DMCreateMassMatrixLumped_Plex;
5726   dm->ops->creategradientmatrix      = DMCreateGradientMatrix_Plex;
5727   dm->ops->createinjection           = DMCreateInjection_Plex;
5728   dm->ops->refine                    = DMRefine_Plex;
5729   dm->ops->coarsen                   = DMCoarsen_Plex;
5730   dm->ops->refinehierarchy           = DMRefineHierarchy_Plex;
5731   dm->ops->coarsenhierarchy          = DMCoarsenHierarchy_Plex;
5732   dm->ops->extrude                   = DMExtrude_Plex;
5733   dm->ops->globaltolocalbegin        = NULL;
5734   dm->ops->globaltolocalend          = NULL;
5735   dm->ops->localtoglobalbegin        = NULL;
5736   dm->ops->localtoglobalend          = NULL;
5737   dm->ops->destroy                   = DMDestroy_Plex;
5738   dm->ops->createsubdm               = DMCreateSubDM_Plex;
5739   dm->ops->createsuperdm             = DMCreateSuperDM_Plex;
5740   dm->ops->getdimpoints              = DMGetDimPoints_Plex;
5741   dm->ops->locatepoints              = DMLocatePoints_Plex;
5742   dm->ops->projectfunctionlocal      = DMProjectFunctionLocal_Plex;
5743   dm->ops->projectfunctionlabellocal = DMProjectFunctionLabelLocal_Plex;
5744   dm->ops->projectfieldlocal         = DMProjectFieldLocal_Plex;
5745   dm->ops->projectfieldlabellocal    = DMProjectFieldLabelLocal_Plex;
5746   dm->ops->projectbdfieldlabellocal  = DMProjectBdFieldLabelLocal_Plex;
5747   dm->ops->computel2diff             = DMComputeL2Diff_Plex;
5748   dm->ops->computel2gradientdiff     = DMComputeL2GradientDiff_Plex;
5749   dm->ops->computel2fielddiff        = DMComputeL2FieldDiff_Plex;
5750   dm->ops->getneighbors              = DMGetNeighbors_Plex;
5751   dm->ops->getlocalboundingbox       = DMGetLocalBoundingBox_Coordinates;
5752   dm->ops->createdomaindecomposition = DMCreateDomainDecomposition_Plex;
5753   dm->ops->createddscatters          = DMCreateDomainDecompositionScatters_Plex;
5754   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", DMPlexInsertBoundaryValues_Plex));
5755   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerivativeBoundaryValues_C", DMPlexInsertTimeDerivativeBoundaryValues_Plex));
5756   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBounds_C", DMPlexInsertBounds_Plex));
5757   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", DMSetUpGLVisViewer_Plex));
5758   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", DMCreateNeumannOverlap_Plex));
5759   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", DMPlexDistributeGetDefault_Plex));
5760   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", DMPlexDistributeSetDefault_Plex));
5761   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", DMPlexReorderGetDefault_Plex));
5762   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", DMPlexReorderSetDefault_Plex));
5763   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetDefault_C", DMReorderSectionGetDefault_Plex));
5764   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetDefault_C", DMReorderSectionSetDefault_Plex));
5765   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetType_C", DMReorderSectionGetType_Plex));
5766   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetType_C", DMReorderSectionSetType_Plex));
5767   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", DMInterpolateSolution_Plex));
5768   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", DMPlexGetOverlap_Plex));
5769   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", DMPlexSetOverlap_Plex));
5770   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetUseCeed_C", DMPlexGetUseCeed_Plex));
5771   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetUseCeed_C", DMPlexSetUseCeed_Plex));
5772   PetscFunctionReturn(PETSC_SUCCESS);
5773 }
5774 
5775 PETSC_INTERN PetscErrorCode DMClone_Plex(DM dm, DM *newdm)
5776 {
5777   DM_Plex       *mesh = (DM_Plex *)dm->data;
5778   const PetscSF *face_sfs;
5779   PetscInt       num_face_sfs;
5780 
5781   PetscFunctionBegin;
5782   mesh->refct++;
5783   (*newdm)->data = mesh;
5784   PetscCall(DMPlexGetIsoperiodicFaceSF(dm, &num_face_sfs, &face_sfs));
5785   PetscCall(DMPlexSetIsoperiodicFaceSF(*newdm, num_face_sfs, (PetscSF *)face_sfs));
5786   PetscCall(PetscObjectChangeTypeName((PetscObject)*newdm, DMPLEX));
5787   PetscCall(DMInitialize_Plex(*newdm));
5788   PetscFunctionReturn(PETSC_SUCCESS);
5789 }
5790 
5791 /*MC
5792   DMPLEX = "plex" - A `DM` object that encapsulates an unstructured mesh (or grid), or CW Complex {cite}`cwcomplex`,
5793            which can be expressed using a Hasse Diagram {cite}`hassediagram`.
5794            In the local representation, `Vec`s contain all unknowns in the interior and shared boundary. This is
5795            specified by a `PetscSection` object. Ownership in the global representation is determined by
5796            ownership of the underlying `DMPLEX` points. This is specified by another `PetscSection` object.
5797 
5798   Options Database Keys:
5799 + -dm_refine_pre                     - Refine mesh before distribution
5800 + -dm_refine_uniform_pre             - Choose uniform or generator-based refinement
5801 + -dm_refine_volume_limit_pre        - Cell volume limit after pre-refinement using generator
5802 . -dm_distribute                     - Distribute mesh across processes
5803 . -dm_distribute_overlap             - Number of cells to overlap for distribution
5804 . -dm_refine                         - Refine mesh after distribution
5805 . -dm_localize <bool>                - Whether to localize coordinates for periodic meshes
5806 . -dm_sparse_localize <bool>         - Whether to only localize cells on the periodic boundary
5807 . -dm_plex_hash_location             - Use grid hashing for point location
5808 . -dm_plex_hash_box_faces <n,m,p>    - The number of divisions in each direction of the grid hash
5809 . -dm_plex_partition_balance         - Attempt to evenly divide points on partition boundary between processes
5810 . -dm_plex_remesh_bd                 - Allow changes to the boundary on remeshing
5811 . -dm_plex_max_projection_height     - Maximum mesh point height used to project locally
5812 . -dm_plex_regular_refinement        - Use special nested projection algorithm for regular refinement
5813 . -dm_plex_reorder_section           - Use specialized blocking if available
5814 . -dm_plex_check_all                 - Perform all checks below
5815 . -dm_plex_check_symmetry            - Check that the adjacency information in the mesh is symmetric
5816 . -dm_plex_check_skeleton <celltype> - Check that each cell has the correct number of vertices
5817 . -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
5818 . -dm_plex_check_geometry            - Check that cells have positive volume
5819 . -dm_view :mesh.tex:ascii_latex     - View the mesh in LaTeX/TikZ
5820 . -dm_plex_view_scale <num>          - Scale the TikZ
5821 . -dm_plex_print_fem <num>           - View FEM assembly information, such as element vectors and matrices
5822 - -dm_plex_print_fvm <num>           - View FVM assembly information, such as flux updates
5823 
5824   Level: intermediate
5825 
5826 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMType`, `DMPlexCreate()`, `DMCreate()`, `DMSetType()`, `PetscSection`
5827 M*/
5828 
5829 PETSC_EXTERN PetscErrorCode DMCreate_Plex(DM dm)
5830 {
5831   DM_Plex *mesh;
5832   PetscInt unit;
5833 
5834   PetscFunctionBegin;
5835   PetscCall(PetscCitationsRegister(PlexCitation, &Plexcite));
5836   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5837   PetscCall(PetscNew(&mesh));
5838   dm->reorderSection = DM_REORDER_DEFAULT_NOTSET;
5839   dm->data           = mesh;
5840 
5841   mesh->refct = 1;
5842   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &mesh->coneSection));
5843   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &mesh->supportSection));
5844   mesh->refinementUniform       = PETSC_TRUE;
5845   mesh->refinementLimit         = -1.0;
5846   mesh->distDefault             = PETSC_TRUE;
5847   mesh->reorderDefault          = DM_REORDER_DEFAULT_NOTSET;
5848   mesh->distributionName        = NULL;
5849   mesh->interpolated            = DMPLEX_INTERPOLATED_INVALID;
5850   mesh->interpolatedCollective  = DMPLEX_INTERPOLATED_INVALID;
5851   mesh->interpolatePreferTensor = PETSC_TRUE;
5852 
5853   PetscCall(PetscPartitionerCreate(PetscObjectComm((PetscObject)dm), &mesh->partitioner));
5854   mesh->remeshBd = PETSC_FALSE;
5855 
5856   for (unit = 0; unit < NUM_PETSC_UNITS; ++unit) mesh->scale[unit] = 1.0;
5857 
5858   mesh->depthState    = -1;
5859   mesh->celltypeState = -1;
5860   mesh->printTol      = 1.0e-10;
5861   mesh->nonempty_comm = MPI_COMM_SELF;
5862 
5863   PetscCall(DMInitialize_Plex(dm));
5864   PetscFunctionReturn(PETSC_SUCCESS);
5865 }
5866 
5867 /*@
5868   DMPlexCreate - Creates a `DMPLEX` object, which encapsulates an unstructured mesh, or CW complex, which can be expressed using a Hasse Diagram.
5869 
5870   Collective
5871 
5872   Input Parameter:
5873 . comm - The communicator for the `DMPLEX` object
5874 
5875   Output Parameter:
5876 . mesh - The `DMPLEX` object
5877 
5878   Level: beginner
5879 
5880 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMType`, `DMCreate()`, `DMSetType()`
5881 @*/
5882 PetscErrorCode DMPlexCreate(MPI_Comm comm, DM *mesh)
5883 {
5884   PetscFunctionBegin;
5885   PetscAssertPointer(mesh, 2);
5886   PetscCall(DMCreate(comm, mesh));
5887   PetscCall(DMSetType(*mesh, DMPLEX));
5888   PetscFunctionReturn(PETSC_SUCCESS);
5889 }
5890 
5891 /*@C
5892   DMPlexBuildFromCellListParallel - Build distributed `DMPLEX` topology from a list of vertices for each cell (common mesh generator output) where all cells have the same celltype
5893 
5894   Collective; No Fortran Support
5895 
5896   Input Parameters:
5897 + dm          - The `DM`
5898 . numCells    - The number of cells owned by this process
5899 . numVertices - The number of vertices to be owned by this process, or `PETSC_DECIDE`
5900 . NVertices   - The global number of vertices, or `PETSC_DETERMINE`
5901 . numCorners  - The number of vertices for each cell
5902 - cells       - An array of numCells*numCorners numbers, the global vertex numbers for each cell
5903 
5904   Output Parameters:
5905 + vertexSF         - (Optional) `PetscSF` describing complete vertex ownership
5906 - verticesAdjSaved - (Optional) vertex adjacency array
5907 
5908   Level: advanced
5909 
5910   Notes:
5911   Two triangles sharing a face
5912 .vb
5913 
5914         2
5915       / | \
5916      /  |  \
5917     /   |   \
5918    0  0 | 1  3
5919     \   |   /
5920      \  |  /
5921       \ | /
5922         1
5923 .ve
5924   would have input
5925 .vb
5926   numCells = 2, numVertices = 4
5927   cells = [0 1 2  1 3 2]
5928 .ve
5929   which would result in the `DMPLEX`
5930 .vb
5931 
5932         4
5933       / | \
5934      /  |  \
5935     /   |   \
5936    2  0 | 1  5
5937     \   |   /
5938      \  |  /
5939       \ | /
5940         3
5941 .ve
5942 
5943   Vertices are implicitly numbered consecutively 0,...,NVertices.
5944   Each rank owns a chunk of numVertices consecutive vertices.
5945   If numVertices is `PETSC_DECIDE`, PETSc will distribute them as evenly as possible using PetscLayout.
5946   If NVertices is `PETSC_DETERMINE` and numVertices is PETSC_DECIDE, NVertices is computed by PETSc as the maximum vertex index in cells + 1.
5947   If only NVertices is `PETSC_DETERMINE`, it is computed as the sum of numVertices over all ranks.
5948 
5949   The cell distribution is arbitrary non-overlapping, independent of the vertex distribution.
5950 
5951 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexBuildFromCellList()`, `DMPlexCreateFromCellListParallelPetsc()`, `DMPlexBuildCoordinatesFromCellListParallel()`,
5952           `PetscSF`
5953 @*/
5954 PetscErrorCode DMPlexBuildFromCellListParallel(DM dm, PetscInt numCells, PetscInt numVertices, PetscInt NVertices, PetscInt numCorners, const PetscInt cells[], PeOp PetscSF *vertexSF, PeOp PetscInt *verticesAdjSaved[])
5955 {
5956   PetscSF     sfPoint;
5957   PetscLayout layout;
5958   PetscInt    numVerticesAdj, *verticesAdj, *cones, c, p;
5959 
5960   PetscFunctionBegin;
5961   PetscValidLogicalCollectiveInt(dm, NVertices, 4);
5962   PetscCall(PetscLogEventBegin(DMPLEX_BuildFromCellList, dm, 0, 0, 0));
5963   /* Get/check global number of vertices */
5964   {
5965     PetscInt       NVerticesInCells, i;
5966     const PetscInt len = numCells * numCorners;
5967 
5968     /* NVerticesInCells = max(cells) + 1 */
5969     NVerticesInCells = PETSC_INT_MIN;
5970     for (i = 0; i < len; i++)
5971       if (cells[i] > NVerticesInCells) NVerticesInCells = cells[i];
5972     ++NVerticesInCells;
5973     PetscCallMPI(MPIU_Allreduce(MPI_IN_PLACE, &NVerticesInCells, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
5974 
5975     if (numVertices == PETSC_DECIDE && NVertices == PETSC_DECIDE) NVertices = NVerticesInCells;
5976     else
5977       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);
5978   }
5979   /* Count locally unique vertices */
5980   {
5981     PetscHSetI vhash;
5982     PetscInt   off = 0;
5983 
5984     PetscCall(PetscHSetICreate(&vhash));
5985     for (c = 0; c < numCells; ++c) {
5986       for (p = 0; p < numCorners; ++p) PetscCall(PetscHSetIAdd(vhash, cells[c * numCorners + p]));
5987     }
5988     PetscCall(PetscHSetIGetSize(vhash, &numVerticesAdj));
5989     if (!verticesAdjSaved) PetscCall(PetscMalloc1(numVerticesAdj, &verticesAdj));
5990     else verticesAdj = *verticesAdjSaved;
5991     PetscCall(PetscHSetIGetElems(vhash, &off, verticesAdj));
5992     PetscCall(PetscHSetIDestroy(&vhash));
5993     PetscCheck(off == numVerticesAdj, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Invalid number of local vertices %" PetscInt_FMT " should be %" PetscInt_FMT, off, numVerticesAdj);
5994   }
5995   PetscCall(PetscSortInt(numVerticesAdj, verticesAdj));
5996   /* Create cones */
5997   PetscCall(DMPlexSetChart(dm, 0, numCells + numVerticesAdj));
5998   for (c = 0; c < numCells; ++c) PetscCall(DMPlexSetConeSize(dm, c, numCorners));
5999   PetscCall(DMSetUp(dm));
6000   PetscCall(DMPlexGetCones(dm, &cones));
6001   for (c = 0; c < numCells; ++c) {
6002     for (p = 0; p < numCorners; ++p) {
6003       const PetscInt gv = cells[c * numCorners + p];
6004       PetscInt       lv;
6005 
6006       /* Positions within verticesAdj form 0-based local vertex numbering;
6007          we need to shift it by numCells to get correct DAG points (cells go first) */
6008       PetscCall(PetscFindInt(gv, numVerticesAdj, verticesAdj, &lv));
6009       PetscCheck(lv >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Could not find global vertex %" PetscInt_FMT " in local connectivity", gv);
6010       cones[c * numCorners + p] = lv + numCells;
6011     }
6012   }
6013   /* Build point sf */
6014   PetscCall(PetscLayoutCreate(PetscObjectComm((PetscObject)dm), &layout));
6015   PetscCall(PetscLayoutSetSize(layout, NVertices));
6016   PetscCall(PetscLayoutSetLocalSize(layout, numVertices));
6017   PetscCall(PetscLayoutSetBlockSize(layout, 1));
6018   PetscCall(PetscSFCreateByMatchingIndices(layout, numVerticesAdj, verticesAdj, NULL, numCells, numVerticesAdj, verticesAdj, NULL, numCells, vertexSF, &sfPoint));
6019   PetscCall(PetscLayoutDestroy(&layout));
6020   if (!verticesAdjSaved) PetscCall(PetscFree(verticesAdj));
6021   PetscCall(PetscObjectSetName((PetscObject)sfPoint, "point SF"));
6022   if (dm->sf) {
6023     const char *prefix;
6024 
6025     PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm->sf, &prefix));
6026     PetscCall(PetscObjectSetOptionsPrefix((PetscObject)sfPoint, prefix));
6027   }
6028   PetscCall(DMSetPointSF(dm, sfPoint));
6029   PetscCall(PetscSFDestroy(&sfPoint));
6030   if (vertexSF) PetscCall(PetscObjectSetName((PetscObject)*vertexSF, "Vertex Ownership SF"));
6031   /* Fill in the rest of the topology structure */
6032   PetscCall(DMPlexSymmetrize(dm));
6033   PetscCall(DMPlexStratify(dm));
6034   PetscCall(PetscLogEventEnd(DMPLEX_BuildFromCellList, dm, 0, 0, 0));
6035   PetscFunctionReturn(PETSC_SUCCESS);
6036 }
6037 
6038 /*@C
6039   DMPlexBuildFromCellSectionParallel - Build distributed `DMPLEX` topology from a list of vertices for each cell (common mesh generator output) allowing multiple celltypes
6040 
6041   Collective; No Fortran Support
6042 
6043   Input Parameters:
6044 + dm          - The `DM`
6045 . numCells    - The number of cells owned by this process
6046 . numVertices - The number of vertices to be owned by this process, or `PETSC_DECIDE`
6047 . NVertices   - The global number of vertices, or `PETSC_DETERMINE`
6048 . cellSection - The `PetscSection` giving the number of vertices for each cell (layout of cells)
6049 - cells       - An array of the global vertex numbers for each cell
6050 
6051   Output Parameters:
6052 + vertexSF         - (Optional) `PetscSF` describing complete vertex ownership
6053 - verticesAdjSaved - (Optional) vertex adjacency array
6054 
6055   Level: advanced
6056 
6057   Notes:
6058   A triangle and quadrilateral sharing a face
6059 .vb
6060         2----------3
6061       / |          |
6062      /  |          |
6063     /   |          |
6064    0  0 |     1    |
6065     \   |          |
6066      \  |          |
6067       \ |          |
6068         1----------4
6069 .ve
6070   would have input
6071 .vb
6072   numCells = 2, numVertices = 5
6073   cells = [0 1 2  1 4 3 2]
6074 .ve
6075   which would result in the `DMPLEX`
6076 .vb
6077         4----------5
6078       / |          |
6079      /  |          |
6080     /   |          |
6081    2  0 |     1    |
6082     \   |          |
6083      \  |          |
6084       \ |          |
6085         3----------6
6086 .ve
6087 
6088   Vertices are implicitly numbered consecutively 0,...,NVertices.
6089   Each rank owns a chunk of numVertices consecutive vertices.
6090   If numVertices is `PETSC_DECIDE`, PETSc will distribute them as evenly as possible using PetscLayout.
6091   If NVertices is `PETSC_DETERMINE` and numVertices is PETSC_DECIDE, NVertices is computed by PETSc as the maximum vertex index in cells + 1.
6092   If only NVertices is `PETSC_DETERMINE`, it is computed as the sum of numVertices over all ranks.
6093 
6094   The cell distribution is arbitrary non-overlapping, independent of the vertex distribution.
6095 
6096 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexBuildFromCellListParallel()`, `DMPlexCreateFromCellSectionParallel()`, `DMPlexBuildCoordinatesFromCellListParallel()`,
6097           `PetscSF`
6098 @*/
6099 PetscErrorCode DMPlexBuildFromCellSectionParallel(DM dm, PetscInt numCells, PetscInt numVertices, PetscInt NVertices, PetscSection cellSection, const PetscInt cells[], PeOp PetscSF *vertexSF, PeOp PetscInt *verticesAdjSaved[])
6100 {
6101   PetscSF     sfPoint;
6102   PetscLayout layout;
6103   PetscInt    numVerticesAdj, *verticesAdj, *cones, cStart, cEnd, len;
6104 
6105   PetscFunctionBegin;
6106   PetscValidLogicalCollectiveInt(dm, NVertices, 4);
6107   PetscCall(PetscLogEventBegin(DMPLEX_BuildFromCellList, dm, 0, 0, 0));
6108   PetscCall(PetscSectionGetChart(cellSection, &cStart, &cEnd));
6109   PetscCall(PetscSectionGetStorageSize(cellSection, &len));
6110   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);
6111   /* Get/check global number of vertices */
6112   {
6113     PetscInt NVerticesInCells;
6114 
6115     /* NVerticesInCells = max(cells) + 1 */
6116     NVerticesInCells = PETSC_MIN_INT;
6117     for (PetscInt i = 0; i < len; i++)
6118       if (cells[i] > NVerticesInCells) NVerticesInCells = cells[i];
6119     ++NVerticesInCells;
6120     PetscCallMPI(MPIU_Allreduce(MPI_IN_PLACE, &NVerticesInCells, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
6121 
6122     if (numVertices == PETSC_DECIDE && NVertices == PETSC_DECIDE) NVertices = NVerticesInCells;
6123     else
6124       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);
6125   }
6126   /* Count locally unique vertices */
6127   {
6128     PetscHSetI vhash;
6129     PetscInt   off = 0;
6130 
6131     PetscCall(PetscHSetICreate(&vhash));
6132     for (PetscInt i = 0; i < len; i++) PetscCall(PetscHSetIAdd(vhash, cells[i]));
6133     PetscCall(PetscHSetIGetSize(vhash, &numVerticesAdj));
6134     if (!verticesAdjSaved) PetscCall(PetscMalloc1(numVerticesAdj, &verticesAdj));
6135     else verticesAdj = *verticesAdjSaved;
6136     PetscCall(PetscHSetIGetElems(vhash, &off, verticesAdj));
6137     PetscCall(PetscHSetIDestroy(&vhash));
6138     PetscCheck(off == numVerticesAdj, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Invalid number of local vertices %" PetscInt_FMT " should be %" PetscInt_FMT, off, numVerticesAdj);
6139   }
6140   PetscCall(PetscSortInt(numVerticesAdj, verticesAdj));
6141   /* Create cones */
6142   PetscCall(DMPlexSetChart(dm, 0, numCells + numVerticesAdj));
6143   for (PetscInt c = 0; c < numCells; ++c) {
6144     PetscInt dof;
6145 
6146     PetscCall(PetscSectionGetDof(cellSection, c, &dof));
6147     PetscCall(DMPlexSetConeSize(dm, c, dof));
6148   }
6149   PetscCall(DMSetUp(dm));
6150   PetscCall(DMPlexGetCones(dm, &cones));
6151   for (PetscInt c = 0; c < numCells; ++c) {
6152     PetscInt dof, off;
6153 
6154     PetscCall(PetscSectionGetDof(cellSection, c, &dof));
6155     PetscCall(PetscSectionGetOffset(cellSection, c, &off));
6156     for (PetscInt p = off; p < off + dof; ++p) {
6157       const PetscInt gv = cells[p];
6158       PetscInt       lv;
6159 
6160       /* Positions within verticesAdj form 0-based local vertex numbering;
6161          we need to shift it by numCells to get correct DAG points (cells go first) */
6162       PetscCall(PetscFindInt(gv, numVerticesAdj, verticesAdj, &lv));
6163       PetscCheck(lv >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Could not find global vertex %" PetscInt_FMT " in local connectivity", gv);
6164       cones[p] = lv + numCells;
6165     }
6166   }
6167   /* Build point sf */
6168   PetscCall(PetscLayoutCreate(PetscObjectComm((PetscObject)dm), &layout));
6169   PetscCall(PetscLayoutSetSize(layout, NVertices));
6170   PetscCall(PetscLayoutSetLocalSize(layout, numVertices));
6171   PetscCall(PetscLayoutSetBlockSize(layout, 1));
6172   PetscCall(PetscSFCreateByMatchingIndices(layout, numVerticesAdj, verticesAdj, NULL, numCells, numVerticesAdj, verticesAdj, NULL, numCells, vertexSF, &sfPoint));
6173   PetscCall(PetscLayoutDestroy(&layout));
6174   if (!verticesAdjSaved) PetscCall(PetscFree(verticesAdj));
6175   PetscCall(PetscObjectSetName((PetscObject)sfPoint, "point SF"));
6176   if (dm->sf) {
6177     const char *prefix;
6178 
6179     PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm->sf, &prefix));
6180     PetscCall(PetscObjectSetOptionsPrefix((PetscObject)sfPoint, prefix));
6181   }
6182   PetscCall(DMSetPointSF(dm, sfPoint));
6183   PetscCall(PetscSFDestroy(&sfPoint));
6184   if (vertexSF) PetscCall(PetscObjectSetName((PetscObject)*vertexSF, "Vertex Ownership SF"));
6185   /* Fill in the rest of the topology structure */
6186   PetscCall(DMPlexSymmetrize(dm));
6187   PetscCall(DMPlexStratify(dm));
6188   PetscCall(PetscLogEventEnd(DMPLEX_BuildFromCellList, dm, 0, 0, 0));
6189   PetscFunctionReturn(PETSC_SUCCESS);
6190 }
6191 
6192 /*@
6193   DMPlexBuildCoordinatesFromCellListParallel - Build `DM` coordinates from a list of coordinates for each owned vertex (common mesh generator output)
6194 
6195   Collective; No Fortran Support
6196 
6197   Input Parameters:
6198 + dm           - The `DM`
6199 . spaceDim     - The spatial dimension used for coordinates
6200 . sfVert       - `PetscSF` describing complete vertex ownership
6201 - vertexCoords - An array of numVertices*spaceDim numbers, the coordinates of each vertex
6202 
6203   Level: advanced
6204 
6205 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexBuildCoordinatesFromCellList()`, `DMPlexCreateFromCellListParallelPetsc()`, `DMPlexBuildFromCellListParallel()`
6206 @*/
6207 PetscErrorCode DMPlexBuildCoordinatesFromCellListParallel(DM dm, PetscInt spaceDim, PetscSF sfVert, const PetscReal vertexCoords[])
6208 {
6209   PetscSection coordSection;
6210   Vec          coordinates;
6211   PetscScalar *coords;
6212   PetscInt     numVertices, numVerticesAdj, coordSize, v, vStart, vEnd;
6213   PetscMPIInt  spaceDimi;
6214 
6215   PetscFunctionBegin;
6216   PetscCall(PetscLogEventBegin(DMPLEX_BuildCoordinatesFromCellList, dm, 0, 0, 0));
6217   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
6218   PetscCheck(vStart >= 0 && vEnd >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "DM is not set up properly. DMPlexBuildFromCellList() should be called first.");
6219   PetscCall(DMSetCoordinateDim(dm, spaceDim));
6220   PetscCall(PetscSFGetGraph(sfVert, &numVertices, &numVerticesAdj, NULL, NULL));
6221   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);
6222   PetscCall(DMGetCoordinateSection(dm, &coordSection));
6223   PetscCall(PetscSectionSetNumFields(coordSection, 1));
6224   PetscCall(PetscSectionSetFieldComponents(coordSection, 0, spaceDim));
6225   PetscCall(PetscSectionSetChart(coordSection, vStart, vEnd));
6226   for (v = vStart; v < vEnd; ++v) {
6227     PetscCall(PetscSectionSetDof(coordSection, v, spaceDim));
6228     PetscCall(PetscSectionSetFieldDof(coordSection, v, 0, spaceDim));
6229   }
6230   PetscCall(PetscSectionSetUp(coordSection));
6231   PetscCall(PetscSectionGetStorageSize(coordSection, &coordSize));
6232   PetscCall(VecCreate(PetscObjectComm((PetscObject)dm), &coordinates));
6233   PetscCall(VecSetBlockSize(coordinates, spaceDim));
6234   PetscCall(PetscObjectSetName((PetscObject)coordinates, "coordinates"));
6235   PetscCall(VecSetSizes(coordinates, coordSize, PETSC_DETERMINE));
6236   PetscCall(VecSetType(coordinates, VECSTANDARD));
6237   PetscCall(VecGetArray(coordinates, &coords));
6238   {
6239     MPI_Datatype coordtype;
6240 
6241     /* Need a temp buffer for coords if we have complex/single */
6242     PetscCall(PetscMPIIntCast(spaceDim, &spaceDimi));
6243     PetscCallMPI(MPI_Type_contiguous(spaceDimi, MPIU_SCALAR, &coordtype));
6244     PetscCallMPI(MPI_Type_commit(&coordtype));
6245 #if defined(PETSC_USE_COMPLEX)
6246     {
6247       PetscScalar *svertexCoords;
6248       PetscInt     i;
6249       PetscCall(PetscMalloc1(numVertices * spaceDim, &svertexCoords));
6250       for (i = 0; i < numVertices * spaceDim; i++) svertexCoords[i] = vertexCoords[i];
6251       PetscCall(PetscSFBcastBegin(sfVert, coordtype, svertexCoords, coords, MPI_REPLACE));
6252       PetscCall(PetscSFBcastEnd(sfVert, coordtype, svertexCoords, coords, MPI_REPLACE));
6253       PetscCall(PetscFree(svertexCoords));
6254     }
6255 #else
6256     PetscCall(PetscSFBcastBegin(sfVert, coordtype, vertexCoords, coords, MPI_REPLACE));
6257     PetscCall(PetscSFBcastEnd(sfVert, coordtype, vertexCoords, coords, MPI_REPLACE));
6258 #endif
6259     PetscCallMPI(MPI_Type_free(&coordtype));
6260   }
6261   PetscCall(VecRestoreArray(coordinates, &coords));
6262   PetscCall(DMSetCoordinatesLocal(dm, coordinates));
6263   PetscCall(VecDestroy(&coordinates));
6264   PetscCall(PetscLogEventEnd(DMPLEX_BuildCoordinatesFromCellList, dm, 0, 0, 0));
6265   PetscFunctionReturn(PETSC_SUCCESS);
6266 }
6267 
6268 /*@
6269   DMPlexCreateFromCellListParallelPetsc - Create distributed `DMPLEX` from a list of vertices for each cell (common mesh generator output) where all cells have the same celltype
6270 
6271   Collective
6272 
6273   Input Parameters:
6274 + comm         - The communicator
6275 . dim          - The topological dimension of the mesh
6276 . numCells     - The number of cells owned by this process
6277 . numVertices  - The number of vertices owned by this process, or `PETSC_DECIDE`
6278 . NVertices    - The global number of vertices, or `PETSC_DECIDE`
6279 . numCorners   - The number of vertices for each cell
6280 . interpolate  - Flag indicating that intermediate mesh entities (faces, edges) should be created automatically
6281 . cells        - An array of numCells*numCorners numbers, the global vertex numbers for each cell
6282 . spaceDim     - The spatial dimension used for coordinates
6283 - vertexCoords - An array of numVertices*spaceDim numbers, the coordinates of each vertex
6284 
6285   Output Parameters:
6286 + dm          - The `DM`
6287 . vertexSF    - (Optional) `PetscSF` describing complete vertex ownership
6288 - verticesAdj - (Optional) vertex adjacency array
6289 
6290   Level: intermediate
6291 
6292   Notes:
6293   This function is just a convenient sequence of `DMCreate()`, `DMSetType()`, `DMSetDimension()`,
6294   `DMPlexBuildFromCellListParallel()`, `DMPlexInterpolate()`, `DMPlexBuildCoordinatesFromCellListParallel()`
6295 
6296   See `DMPlexBuildFromCellListParallel()` for an example and details about the topology-related parameters.
6297 
6298   See `DMPlexBuildCoordinatesFromCellListParallel()` for details about the geometry-related parameters.
6299 
6300 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateFromCellListPetsc()`, `DMPlexBuildFromCellListParallel()`, `DMPlexBuildCoordinatesFromCellListParallel()`, `DMPlexCreateFromDAG()`, `DMPlexCreate()`
6301 @*/
6302 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)
6303 {
6304   PetscSF sfVert;
6305 
6306   PetscFunctionBegin;
6307   PetscCall(DMCreate(comm, dm));
6308   PetscCall(DMSetType(*dm, DMPLEX));
6309   PetscValidLogicalCollectiveInt(*dm, dim, 2);
6310   PetscValidLogicalCollectiveInt(*dm, spaceDim, 9);
6311   PetscCall(DMSetDimension(*dm, dim));
6312   PetscCall(DMPlexBuildFromCellListParallel(*dm, numCells, numVertices, NVertices, numCorners, cells, &sfVert, verticesAdj));
6313   if (interpolate) {
6314     DM idm;
6315 
6316     PetscCall(DMPlexInterpolate(*dm, &idm));
6317     PetscCall(DMDestroy(dm));
6318     *dm = idm;
6319   }
6320   PetscCall(DMPlexBuildCoordinatesFromCellListParallel(*dm, spaceDim, sfVert, vertexCoords));
6321   if (vertexSF) *vertexSF = sfVert;
6322   else PetscCall(PetscSFDestroy(&sfVert));
6323   PetscFunctionReturn(PETSC_SUCCESS);
6324 }
6325 
6326 /*@
6327   DMPlexCreateFromCellSectionParallel - Create distributed `DMPLEX` from a list of vertices for each cell (common mesh generator output) and supports multiple celltypes
6328 
6329   Collective
6330 
6331   Input Parameters:
6332 + comm         - The communicator
6333 . dim          - The topological dimension of the mesh
6334 . numCells     - The number of cells owned by this process
6335 . numVertices  - The number of vertices owned by this process, or `PETSC_DECIDE`
6336 . NVertices    - The global number of vertices, or `PETSC_DECIDE`
6337 . cellSection  - The `PetscSection` giving the number of vertices for each cell (layout of cells)
6338 . interpolate  - Flag indicating that intermediate mesh entities (faces, edges) should be created automatically
6339 . cells        - An array of the global vertex numbers for each cell
6340 . spaceDim     - The spatial dimension used for coordinates
6341 - vertexCoords - An array of numVertices*spaceDim numbers, the coordinates of each vertex
6342 
6343   Output Parameters:
6344 + dm          - The `DM`
6345 . vertexSF    - (Optional) `PetscSF` describing complete vertex ownership
6346 - verticesAdj - (Optional) vertex adjacency array
6347 
6348   Level: intermediate
6349 
6350   Notes:
6351   This function is just a convenient sequence of `DMCreate()`, `DMSetType()`, `DMSetDimension()`,
6352   `DMPlexBuildFromCellSectionParallel()`, `DMPlexInterpolate()`, `DMPlexBuildCoordinatesFromCellListParallel()`
6353 
6354   See `DMPlexBuildFromCellSectionParallel()` for an example and details about the topology-related parameters.
6355 
6356   See `DMPlexBuildCoordinatesFromCellListParallel()` for details about the geometry-related parameters.
6357 
6358 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateFromCellListPetsc()`, `DMPlexBuildFromCellListParallel()`, `DMPlexBuildCoordinatesFromCellListParallel()`, `DMPlexCreateFromDAG()`, `DMPlexCreate()`
6359 @*/
6360 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)
6361 {
6362   PetscSF sfVert;
6363 
6364   PetscFunctionBegin;
6365   PetscCall(DMCreate(comm, dm));
6366   PetscCall(DMSetType(*dm, DMPLEX));
6367   PetscValidLogicalCollectiveInt(*dm, dim, 2);
6368   PetscValidLogicalCollectiveInt(*dm, spaceDim, 9);
6369   PetscCall(DMSetDimension(*dm, dim));
6370   PetscCall(DMPlexBuildFromCellSectionParallel(*dm, numCells, numVertices, NVertices, cellSection, cells, &sfVert, verticesAdj));
6371   if (interpolate) {
6372     DM idm;
6373 
6374     PetscCall(DMPlexInterpolate(*dm, &idm));
6375     PetscCall(DMDestroy(dm));
6376     *dm = idm;
6377   }
6378   PetscCall(DMPlexBuildCoordinatesFromCellListParallel(*dm, spaceDim, sfVert, vertexCoords));
6379   if (vertexSF) *vertexSF = sfVert;
6380   else PetscCall(PetscSFDestroy(&sfVert));
6381   PetscFunctionReturn(PETSC_SUCCESS);
6382 }
6383 
6384 /*@
6385   DMPlexBuildFromCellList - Build `DMPLEX` topology from a list of vertices for each cell (common mesh generator output)
6386 
6387   Collective; No Fortran Support
6388 
6389   Input Parameters:
6390 + dm          - The `DM`
6391 . numCells    - The number of cells owned by this process
6392 . numVertices - The number of vertices owned by this process, or `PETSC_DETERMINE`
6393 . numCorners  - The number of vertices for each cell
6394 - cells       - An array of `numCells` x `numCorners` numbers, the global vertex numbers for each cell
6395 
6396   Level: advanced
6397 
6398   Notes:
6399   Two triangles sharing a face
6400 .vb
6401 
6402         2
6403       / | \
6404      /  |  \
6405     /   |   \
6406    0  0 | 1  3
6407     \   |   /
6408      \  |  /
6409       \ | /
6410         1
6411 .ve
6412   would have input
6413 .vb
6414   numCells = 2, numVertices = 4
6415   cells = [0 1 2  1 3 2]
6416 .ve
6417   which would result in the `DMPLEX`
6418 .vb
6419 
6420         4
6421       / | \
6422      /  |  \
6423     /   |   \
6424    2  0 | 1  5
6425     \   |   /
6426      \  |  /
6427       \ | /
6428         3
6429 .ve
6430 
6431   If numVertices is `PETSC_DETERMINE`, it is computed by PETSc as the maximum vertex index in cells + 1.
6432 
6433 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexBuildFromCellListParallel()`, `DMPlexBuildCoordinatesFromCellList()`, `DMPlexCreateFromCellListPetsc()`
6434 @*/
6435 PetscErrorCode DMPlexBuildFromCellList(DM dm, PetscInt numCells, PetscInt numVertices, PetscInt numCorners, const PetscInt cells[])
6436 {
6437   PetscInt *cones, c, p, dim;
6438 
6439   PetscFunctionBegin;
6440   PetscCall(PetscLogEventBegin(DMPLEX_BuildFromCellList, dm, 0, 0, 0));
6441   PetscCall(DMGetDimension(dm, &dim));
6442   /* Get/check global number of vertices */
6443   {
6444     PetscInt       NVerticesInCells, i;
6445     const PetscInt len = numCells * numCorners;
6446 
6447     /* NVerticesInCells = max(cells) + 1 */
6448     NVerticesInCells = PETSC_INT_MIN;
6449     for (i = 0; i < len; i++)
6450       if (cells[i] > NVerticesInCells) NVerticesInCells = cells[i];
6451     ++NVerticesInCells;
6452 
6453     if (numVertices == PETSC_DECIDE) numVertices = NVerticesInCells;
6454     else
6455       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);
6456   }
6457   PetscCall(DMPlexSetChart(dm, 0, numCells + numVertices));
6458   for (c = 0; c < numCells; ++c) PetscCall(DMPlexSetConeSize(dm, c, numCorners));
6459   PetscCall(DMSetUp(dm));
6460   PetscCall(DMPlexGetCones(dm, &cones));
6461   for (c = 0; c < numCells; ++c) {
6462     for (p = 0; p < numCorners; ++p) cones[c * numCorners + p] = cells[c * numCorners + p] + numCells;
6463   }
6464   PetscCall(DMPlexSymmetrize(dm));
6465   PetscCall(DMPlexStratify(dm));
6466   PetscCall(PetscLogEventEnd(DMPLEX_BuildFromCellList, dm, 0, 0, 0));
6467   PetscFunctionReturn(PETSC_SUCCESS);
6468 }
6469 
6470 /*@
6471   DMPlexBuildCoordinatesFromCellList - Build `DM` coordinates from a list of coordinates for each owned vertex (common mesh generator output)
6472 
6473   Collective
6474 
6475   Input Parameters:
6476 + dm           - The `DM`
6477 . spaceDim     - The spatial dimension used for coordinates
6478 - vertexCoords - An array of numVertices*spaceDim numbers, the coordinates of each vertex
6479 
6480   Level: advanced
6481 
6482 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexBuildCoordinatesFromCellListParallel()`, `DMPlexCreateFromCellListPetsc()`, `DMPlexBuildFromCellList()`
6483 @*/
6484 PetscErrorCode DMPlexBuildCoordinatesFromCellList(DM dm, PetscInt spaceDim, const PetscReal vertexCoords[])
6485 {
6486   PetscSection coordSection;
6487   Vec          coordinates;
6488   DM           cdm;
6489   PetscScalar *coords;
6490   PetscInt     v, vStart, vEnd, d;
6491 
6492   PetscFunctionBegin;
6493   PetscCall(PetscLogEventBegin(DMPLEX_BuildCoordinatesFromCellList, dm, 0, 0, 0));
6494   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
6495   PetscCheck(vStart >= 0 && vEnd >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "DM is not set up properly. DMPlexBuildFromCellList() should be called first.");
6496   PetscCall(DMSetCoordinateDim(dm, spaceDim));
6497   PetscCall(DMGetCoordinateSection(dm, &coordSection));
6498   PetscCall(PetscSectionSetNumFields(coordSection, 1));
6499   PetscCall(PetscSectionSetFieldComponents(coordSection, 0, spaceDim));
6500   PetscCall(PetscSectionSetChart(coordSection, vStart, vEnd));
6501   for (v = vStart; v < vEnd; ++v) {
6502     PetscCall(PetscSectionSetDof(coordSection, v, spaceDim));
6503     PetscCall(PetscSectionSetFieldDof(coordSection, v, 0, spaceDim));
6504   }
6505   PetscCall(PetscSectionSetUp(coordSection));
6506 
6507   PetscCall(DMGetCoordinateDM(dm, &cdm));
6508   PetscCall(DMCreateLocalVector(cdm, &coordinates));
6509   PetscCall(VecSetBlockSize(coordinates, spaceDim));
6510   PetscCall(PetscObjectSetName((PetscObject)coordinates, "coordinates"));
6511   PetscCall(VecGetArrayWrite(coordinates, &coords));
6512   for (v = 0; v < vEnd - vStart; ++v) {
6513     for (d = 0; d < spaceDim; ++d) coords[v * spaceDim + d] = vertexCoords[v * spaceDim + d];
6514   }
6515   PetscCall(VecRestoreArrayWrite(coordinates, &coords));
6516   PetscCall(DMSetCoordinatesLocal(dm, coordinates));
6517   PetscCall(VecDestroy(&coordinates));
6518   PetscCall(PetscLogEventEnd(DMPLEX_BuildCoordinatesFromCellList, dm, 0, 0, 0));
6519   PetscFunctionReturn(PETSC_SUCCESS);
6520 }
6521 
6522 /*@
6523   DMPlexCreateFromCellListPetsc - Create `DMPLEX` from a list of vertices for each cell (common mesh generator output), but only process 0 takes in the input
6524 
6525   Collective
6526 
6527   Input Parameters:
6528 + comm         - The communicator
6529 . dim          - The topological dimension of the mesh
6530 . numCells     - The number of cells, only on process 0
6531 . numVertices  - The number of vertices owned by this process, or `PETSC_DECIDE`, only on process 0
6532 . numCorners   - The number of vertices for each cell, only on process 0
6533 . interpolate  - Flag indicating that intermediate mesh entities (faces, edges) should be created automatically
6534 . cells        - An array of $ numCells \times numCorners$ numbers, the vertices for each cell, only on process 0
6535 . spaceDim     - The spatial dimension used for coordinates
6536 - vertexCoords - An array of $ numVertices \times spaceDim$ numbers, the coordinates of each vertex, only on process 0
6537 
6538   Output Parameter:
6539 . dm - The `DM`, which only has points on process 0
6540 
6541   Level: intermediate
6542 
6543   Notes:
6544   This function is just a convenient sequence of `DMCreate()`, `DMSetType()`, `DMSetDimension()`, `DMPlexBuildFromCellList()`,
6545   `DMPlexInterpolate()`, `DMPlexBuildCoordinatesFromCellList()`
6546 
6547   See `DMPlexBuildFromCellList()` for an example and details about the topology-related parameters.
6548   See `DMPlexBuildCoordinatesFromCellList()` for details about the geometry-related parameters.
6549   See `DMPlexCreateFromCellListParallelPetsc()` for parallel input
6550 
6551 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateFromCellListParallelPetsc()`, `DMPlexBuildFromCellList()`, `DMPlexBuildCoordinatesFromCellList()`, `DMPlexCreateFromDAG()`, `DMPlexCreate()`
6552 @*/
6553 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)
6554 {
6555   PetscMPIInt rank;
6556 
6557   PetscFunctionBegin;
6558   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.");
6559   PetscCallMPI(MPI_Comm_rank(comm, &rank));
6560   PetscCall(DMCreate(comm, dm));
6561   PetscCall(DMSetType(*dm, DMPLEX));
6562   PetscCall(DMSetDimension(*dm, dim));
6563   if (rank == 0) PetscCall(DMPlexBuildFromCellList(*dm, numCells, numVertices, numCorners, cells));
6564   else PetscCall(DMPlexBuildFromCellList(*dm, 0, 0, 0, NULL));
6565   if (interpolate) {
6566     DM idm;
6567 
6568     PetscCall(DMPlexInterpolate(*dm, &idm));
6569     PetscCall(DMDestroy(dm));
6570     *dm = idm;
6571   }
6572   if (rank == 0) PetscCall(DMPlexBuildCoordinatesFromCellList(*dm, spaceDim, vertexCoords));
6573   else PetscCall(DMPlexBuildCoordinatesFromCellList(*dm, spaceDim, NULL));
6574   PetscFunctionReturn(PETSC_SUCCESS);
6575 }
6576 
6577 /*@
6578   DMPlexCreateFromDAG - This takes as input the adjacency-list representation of the Directed Acyclic Graph (Hasse Diagram) encoding a mesh, and produces a `DM`
6579 
6580   Input Parameters:
6581 + dm               - The empty `DM` object, usually from `DMCreate()` and `DMSetDimension()`
6582 . depth            - The depth of the DAG
6583 . numPoints        - Array of size $ depth + 1 $ containing the number of points at each `depth`
6584 . coneSize         - The cone size of each point
6585 . cones            - The concatenation of the cone points for each point, the cone list must be oriented correctly for each point
6586 . coneOrientations - The orientation of each cone point
6587 - vertexCoords     - An array of $ numPoints[0] \times spacedim $ numbers representing the coordinates of each vertex, with `spacedim` the value set via `DMSetCoordinateDim()`
6588 
6589   Output Parameter:
6590 . dm - The `DM`
6591 
6592   Level: advanced
6593 
6594   Note:
6595   Two triangles sharing a face would have input
6596 .vb
6597   depth = 1, numPoints = [4 2], coneSize = [3 3 0 0 0 0]
6598   cones = [2 3 4  3 5 4], coneOrientations = [0 0 0  0 0 0]
6599  vertexCoords = [-1.0 0.0  0.0 -1.0  0.0 1.0  1.0 0.0]
6600 .ve
6601   which would result in the DMPlex
6602 .vb
6603         4
6604       / | \
6605      /  |  \
6606     /   |   \
6607    2  0 | 1  5
6608     \   |   /
6609      \  |  /
6610       \ | /
6611         3
6612 .ve
6613   Notice that all points are numbered consecutively, unlike `DMPlexCreateFromCellListPetsc()`
6614 
6615   Developer Note:
6616   This does not create anything so should not have create in the name.
6617 
6618 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateFromCellListPetsc()`, `DMPlexCreate()`
6619 @*/
6620 PetscErrorCode DMPlexCreateFromDAG(DM dm, PetscInt depth, const PetscInt numPoints[], const PetscInt coneSize[], const PetscInt cones[], const PetscInt coneOrientations[], const PetscScalar vertexCoords[])
6621 {
6622   Vec          coordinates;
6623   PetscSection coordSection;
6624   PetscScalar *coords;
6625   PetscInt     coordSize, firstVertex = -1, pStart = 0, pEnd = 0, p, v, dim, dimEmbed, d, off;
6626 
6627   PetscFunctionBegin;
6628   PetscCall(DMGetDimension(dm, &dim));
6629   PetscCall(DMGetCoordinateDim(dm, &dimEmbed));
6630   PetscCheck(dimEmbed >= dim, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Embedding dimension %" PetscInt_FMT " cannot be less than intrinsic dimension %" PetscInt_FMT, dimEmbed, dim);
6631   for (d = 0; d <= depth; ++d) pEnd += numPoints[d];
6632   PetscCall(DMPlexSetChart(dm, pStart, pEnd));
6633   for (p = pStart; p < pEnd; ++p) {
6634     PetscCall(DMPlexSetConeSize(dm, p, coneSize[p - pStart]));
6635     if (firstVertex < 0 && !coneSize[p - pStart]) firstVertex = p - pStart;
6636   }
6637   PetscCheck(firstVertex >= 0 || !numPoints[0], PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Expected %" PetscInt_FMT " vertices but could not find any", numPoints[0]);
6638   PetscCall(DMSetUp(dm)); /* Allocate space for cones */
6639   for (p = pStart, off = 0; p < pEnd; off += coneSize[p - pStart], ++p) {
6640     PetscCall(DMPlexSetCone(dm, p, &cones[off]));
6641     PetscCall(DMPlexSetConeOrientation(dm, p, &coneOrientations[off]));
6642   }
6643   PetscCall(DMPlexSymmetrize(dm));
6644   PetscCall(DMPlexStratify(dm));
6645   /* Build coordinates */
6646   PetscCall(DMGetCoordinateSection(dm, &coordSection));
6647   PetscCall(PetscSectionSetNumFields(coordSection, 1));
6648   PetscCall(PetscSectionSetFieldComponents(coordSection, 0, dimEmbed));
6649   PetscCall(PetscSectionSetChart(coordSection, firstVertex, firstVertex + numPoints[0]));
6650   for (v = firstVertex; v < firstVertex + numPoints[0]; ++v) {
6651     PetscCall(PetscSectionSetDof(coordSection, v, dimEmbed));
6652     PetscCall(PetscSectionSetFieldDof(coordSection, v, 0, dimEmbed));
6653   }
6654   PetscCall(PetscSectionSetUp(coordSection));
6655   PetscCall(PetscSectionGetStorageSize(coordSection, &coordSize));
6656   PetscCall(VecCreate(PETSC_COMM_SELF, &coordinates));
6657   PetscCall(PetscObjectSetName((PetscObject)coordinates, "coordinates"));
6658   PetscCall(VecSetSizes(coordinates, coordSize, PETSC_DETERMINE));
6659   PetscCall(VecSetBlockSize(coordinates, PetscMax(dimEmbed, 1)));
6660   PetscCall(VecSetType(coordinates, VECSTANDARD));
6661   if (vertexCoords) {
6662     PetscCall(VecGetArray(coordinates, &coords));
6663     for (v = 0; v < numPoints[0]; ++v) {
6664       PetscInt off;
6665 
6666       PetscCall(PetscSectionGetOffset(coordSection, v + firstVertex, &off));
6667       for (d = 0; d < dimEmbed; ++d) coords[off + d] = vertexCoords[v * dimEmbed + d];
6668     }
6669   }
6670   PetscCall(VecRestoreArray(coordinates, &coords));
6671   PetscCall(DMSetCoordinatesLocal(dm, coordinates));
6672   PetscCall(VecDestroy(&coordinates));
6673   PetscFunctionReturn(PETSC_SUCCESS);
6674 }
6675 
6676 /*
6677   DMPlexCreateCellVertexFromFile - Create a `DMPLEX` mesh from a simple cell-vertex file.
6678 
6679   Collective
6680 
6681 + comm        - The MPI communicator
6682 . filename    - Name of the .dat file
6683 - interpolate - Create faces and edges in the mesh
6684 
6685   Output Parameter:
6686 . dm  - The `DM` object representing the mesh
6687 
6688   Level: beginner
6689 
6690   Note:
6691   The format is the simplest possible:
6692 .vb
6693   dim Ne Nv Nc Nl
6694   v_1 v_2 ... v_Nc
6695   ...
6696   x y z marker_1 ... marker_Nl
6697 .ve
6698 
6699   Developer Note:
6700   Should use a `PetscViewer` not a filename
6701 
6702 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateFromFile()`, `DMPlexCreateGmsh()`, `DMPlexCreate()`
6703 */
6704 static PetscErrorCode DMPlexCreateCellVertexFromFile(MPI_Comm comm, const char filename[], PetscBool interpolate, DM *dm)
6705 {
6706   DMLabel      marker;
6707   PetscViewer  viewer;
6708   Vec          coordinates;
6709   PetscSection coordSection;
6710   PetscScalar *coords;
6711   char         line[PETSC_MAX_PATH_LEN];
6712   PetscInt     cdim, coordSize, v, c, d;
6713   PetscMPIInt  rank;
6714   int          snum, dim, Nv, Nc, Ncn, Nl;
6715 
6716   PetscFunctionBegin;
6717   PetscCallMPI(MPI_Comm_rank(comm, &rank));
6718   PetscCall(PetscViewerCreate(comm, &viewer));
6719   PetscCall(PetscViewerSetType(viewer, PETSCVIEWERASCII));
6720   PetscCall(PetscViewerFileSetMode(viewer, FILE_MODE_READ));
6721   PetscCall(PetscViewerFileSetName(viewer, filename));
6722   if (rank == 0) {
6723     PetscCall(PetscViewerRead(viewer, line, 5, NULL, PETSC_STRING));
6724     snum = sscanf(line, "%d %d %d %d %d", &dim, &Nc, &Nv, &Ncn, &Nl);
6725     PetscCheck(snum == 5, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unable to parse cell-vertex file: %s", line);
6726   } else {
6727     Nc = Nv = Ncn = Nl = 0;
6728   }
6729   PetscCallMPI(MPI_Bcast(&dim, 1, MPI_INT, 0, comm));
6730   cdim = dim;
6731   PetscCall(DMCreate(comm, dm));
6732   PetscCall(DMSetType(*dm, DMPLEX));
6733   PetscCall(DMPlexSetChart(*dm, 0, Nc + Nv));
6734   PetscCall(DMSetDimension(*dm, dim));
6735   PetscCall(DMSetCoordinateDim(*dm, cdim));
6736   /* Read topology */
6737   if (rank == 0) {
6738     char     format[PETSC_MAX_PATH_LEN];
6739     PetscInt cone[8];
6740     int      vbuf[8], v;
6741 
6742     for (c = 0; c < Ncn; ++c) {
6743       format[c * 3 + 0] = '%';
6744       format[c * 3 + 1] = 'd';
6745       format[c * 3 + 2] = ' ';
6746     }
6747     format[Ncn * 3 - 1] = '\0';
6748     for (c = 0; c < Nc; ++c) PetscCall(DMPlexSetConeSize(*dm, c, Ncn));
6749     PetscCall(DMSetUp(*dm));
6750     for (c = 0; c < Nc; ++c) {
6751       PetscCall(PetscViewerRead(viewer, line, Ncn, NULL, PETSC_STRING));
6752       switch (Ncn) {
6753       case 2:
6754         snum = sscanf(line, format, &vbuf[0], &vbuf[1]);
6755         break;
6756       case 3:
6757         snum = sscanf(line, format, &vbuf[0], &vbuf[1], &vbuf[2]);
6758         break;
6759       case 4:
6760         snum = sscanf(line, format, &vbuf[0], &vbuf[1], &vbuf[2], &vbuf[3]);
6761         break;
6762       case 6:
6763         snum = sscanf(line, format, &vbuf[0], &vbuf[1], &vbuf[2], &vbuf[3], &vbuf[4], &vbuf[5]);
6764         break;
6765       case 8:
6766         snum = sscanf(line, format, &vbuf[0], &vbuf[1], &vbuf[2], &vbuf[3], &vbuf[4], &vbuf[5], &vbuf[6], &vbuf[7]);
6767         break;
6768       default:
6769         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "No cell shape with %d vertices", Ncn);
6770       }
6771       PetscCheck(snum == Ncn, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unable to parse cell-vertex file: %s", line);
6772       for (v = 0; v < Ncn; ++v) cone[v] = vbuf[v] + Nc;
6773       /* Hexahedra are inverted */
6774       if (Ncn == 8) {
6775         PetscInt tmp = cone[1];
6776         cone[1]      = cone[3];
6777         cone[3]      = tmp;
6778       }
6779       PetscCall(DMPlexSetCone(*dm, c, cone));
6780     }
6781   }
6782   PetscCall(DMPlexSymmetrize(*dm));
6783   PetscCall(DMPlexStratify(*dm));
6784   /* Read coordinates */
6785   PetscCall(DMGetCoordinateSection(*dm, &coordSection));
6786   PetscCall(PetscSectionSetNumFields(coordSection, 1));
6787   PetscCall(PetscSectionSetFieldComponents(coordSection, 0, cdim));
6788   PetscCall(PetscSectionSetChart(coordSection, Nc, Nc + Nv));
6789   for (v = Nc; v < Nc + Nv; ++v) {
6790     PetscCall(PetscSectionSetDof(coordSection, v, cdim));
6791     PetscCall(PetscSectionSetFieldDof(coordSection, v, 0, cdim));
6792   }
6793   PetscCall(PetscSectionSetUp(coordSection));
6794   PetscCall(PetscSectionGetStorageSize(coordSection, &coordSize));
6795   PetscCall(VecCreate(PETSC_COMM_SELF, &coordinates));
6796   PetscCall(PetscObjectSetName((PetscObject)coordinates, "coordinates"));
6797   PetscCall(VecSetSizes(coordinates, coordSize, PETSC_DETERMINE));
6798   PetscCall(VecSetBlockSize(coordinates, cdim));
6799   PetscCall(VecSetType(coordinates, VECSTANDARD));
6800   PetscCall(VecGetArray(coordinates, &coords));
6801   if (rank == 0) {
6802     char   format[PETSC_MAX_PATH_LEN];
6803     double x[3];
6804     int    l, val[3];
6805 
6806     if (Nl) {
6807       for (l = 0; l < Nl; ++l) {
6808         format[l * 3 + 0] = '%';
6809         format[l * 3 + 1] = 'd';
6810         format[l * 3 + 2] = ' ';
6811       }
6812       format[Nl * 3 - 1] = '\0';
6813       PetscCall(DMCreateLabel(*dm, "marker"));
6814       PetscCall(DMGetLabel(*dm, "marker", &marker));
6815     }
6816     for (v = 0; v < Nv; ++v) {
6817       PetscCall(PetscViewerRead(viewer, line, 3 + Nl, NULL, PETSC_STRING));
6818       snum = sscanf(line, "%lg %lg %lg", &x[0], &x[1], &x[2]);
6819       PetscCheck(snum == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unable to parse cell-vertex file: %s", line);
6820       switch (Nl) {
6821       case 0:
6822         snum = 0;
6823         break;
6824       case 1:
6825         snum = sscanf(line, format, &val[0]);
6826         break;
6827       case 2:
6828         snum = sscanf(line, format, &val[0], &val[1]);
6829         break;
6830       case 3:
6831         snum = sscanf(line, format, &val[0], &val[1], &val[2]);
6832         break;
6833       default:
6834         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Request support for %d labels", Nl);
6835       }
6836       PetscCheck(snum == Nl, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unable to parse cell-vertex file: %s", line);
6837       for (d = 0; d < cdim; ++d) coords[v * cdim + d] = x[d];
6838       for (l = 0; l < Nl; ++l) PetscCall(DMLabelSetValue(marker, v + Nc, val[l]));
6839     }
6840   }
6841   PetscCall(VecRestoreArray(coordinates, &coords));
6842   PetscCall(DMSetCoordinatesLocal(*dm, coordinates));
6843   PetscCall(VecDestroy(&coordinates));
6844   PetscCall(PetscViewerDestroy(&viewer));
6845   if (interpolate) {
6846     DM      idm;
6847     DMLabel bdlabel;
6848 
6849     PetscCall(DMPlexInterpolate(*dm, &idm));
6850     PetscCall(DMDestroy(dm));
6851     *dm = idm;
6852 
6853     if (!Nl) {
6854       PetscCall(DMCreateLabel(*dm, "marker"));
6855       PetscCall(DMGetLabel(*dm, "marker", &bdlabel));
6856       PetscCall(DMPlexMarkBoundaryFaces(*dm, PETSC_DETERMINE, bdlabel));
6857       PetscCall(DMPlexLabelComplete(*dm, bdlabel));
6858     }
6859   }
6860   PetscFunctionReturn(PETSC_SUCCESS);
6861 }
6862 
6863 /*
6864   DMPlexCreateSTLFromFile - Create a `DMPLEX` mesh from an STL file.
6865 
6866   Collective
6867 
6868 + comm        - The MPI communicator
6869 . filename    - Name of the .dat file
6870 - interpolate - Create faces and edges in the mesh
6871 
6872   Output Parameter:
6873 . dm  - The `DM` object representing the mesh
6874 
6875   Level: beginner
6876 
6877   Notes:
6878   The binary format is:
6879 .vb
6880   UINT8[80]    - Header               - 80 bytes
6881   UINT32       - Number of triangles  - 04 bytes
6882   foreach triangle                    - 50 bytes
6883     REAL32[3] - Normal vector         - 12 bytes
6884     REAL32[3] - Vertex 1              - 12 bytes
6885     REAL32[3] - Vertex 2              - 12 bytes
6886     REAL32[3] - Vertex 3              - 12 bytes
6887     UINT16    - Attribute byte count  - 02 bytes
6888   end
6889 .ve
6890 
6891   The format is here <https://en.wikipedia.org/wiki/STL_(file_format)>
6892 
6893 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateFromFile()`, `DMPlexCreateGmsh()`, `DMPlexCreate()`
6894 */
6895 static PetscErrorCode DMPlexCreateSTLFromFile(MPI_Comm comm, const char filename[], PetscBool interpolate, DM *dm)
6896 {
6897   PetscViewer  viewer;
6898   Vec          coordinates;
6899   PetscSection coordSection;
6900   PetscScalar *coords;
6901   PetscInt     coordSize;
6902   char         line[PETSC_MAX_PATH_LEN];
6903   PetscBT      bt;
6904   float       *trialCoords = NULL;
6905   PetscInt    *cells       = NULL;
6906   PetscBool    isascii;
6907   PetscMPIInt  rank;
6908   PetscInt     Nc, Nv, nv = 0;
6909 
6910   PetscFunctionBegin;
6911   PetscCallMPI(MPI_Comm_rank(comm, &rank));
6912   PetscCall(PetscViewerCreate(comm, &viewer));
6913   PetscCall(PetscViewerSetType(viewer, PETSCVIEWERBINARY));
6914   PetscCall(PetscViewerFileSetMode(viewer, FILE_MODE_READ));
6915   PetscCall(PetscViewerFileSetName(viewer, filename));
6916   if (rank == 0) {
6917     int num;
6918 
6919     PetscCall(PetscViewerRead(viewer, line, 5, NULL, PETSC_CHAR));
6920     line[5] = 0;
6921     PetscCall(PetscStrncmp(line, "solid", 5, &isascii));
6922     PetscCheck(!isascii, comm, PETSC_ERR_SUP, "No support for ASCII yet");
6923     PetscCall(PetscViewerRead(viewer, line, 75, NULL, PETSC_CHAR));
6924     line[75] = 0;
6925     PetscCall(PetscViewerRead(viewer, &num, 1, NULL, PETSC_INT32));
6926     PetscCall(PetscByteSwap(&num, PETSC_INT32, 1));
6927     Nc = num;
6928     // Read facet coordinates
6929     PetscCall(PetscMalloc1(Nc * 9, &trialCoords));
6930     for (PetscInt c = 0; c < Nc; ++c) {
6931       double    normal[3];
6932       short int dummy;
6933 
6934       PetscCall(PetscViewerRead(viewer, normal, 3, NULL, PETSC_FLOAT));
6935       PetscCall(PetscViewerRead(viewer, &trialCoords[c * 9 + 0], 9, NULL, PETSC_FLOAT));
6936       PetscCall(PetscByteSwap(&trialCoords[c * 9 + 0], PETSC_FLOAT, 9));
6937       PetscCall(PetscViewerRead(viewer, &dummy, 1, NULL, PETSC_SHORT));
6938     }
6939     PetscCall(PetscMalloc1(Nc * 3, &cells));
6940     // Find unique vertices
6941     PetscCall(PetscBTCreate(Nc * 3, &bt));
6942     PetscCall(PetscBTMemzero(Nc * 3, bt));
6943     PetscCall(PetscBTSet(bt, 0));
6944     PetscCall(PetscBTSet(bt, 1));
6945     PetscCall(PetscBTSet(bt, 2));
6946     Nv       = 0;
6947     cells[0] = Nc + Nv++;
6948     cells[1] = Nc + Nv++;
6949     cells[2] = Nc + Nv++;
6950     for (PetscInt v = 3; v < Nc * 3; ++v) {
6951       PetscBool match = PETSC_FALSE;
6952 
6953       for (PetscInt w = 0, x = 0; w < v; ++w) {
6954         if (PetscBTLookup(bt, w)) {
6955           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]) {
6956             match    = PETSC_TRUE;
6957             cells[v] = Nc + x;
6958             break;
6959           }
6960           ++x;
6961         }
6962       }
6963       if (!match) {
6964         PetscCall(PetscBTSet(bt, v));
6965         cells[v] = Nc + Nv++;
6966       }
6967     }
6968   } else {
6969     Nc = Nv = 0;
6970   }
6971   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]);
6972   PetscCall(DMCreate(comm, dm));
6973   PetscCall(DMSetType(*dm, DMPLEX));
6974   PetscCall(DMPlexSetChart(*dm, 0, Nc + Nv));
6975   PetscCall(DMSetDimension(*dm, 2));
6976   PetscCall(DMSetCoordinateDim(*dm, 3));
6977   for (PetscInt c = 0; c < Nc; ++c) PetscCall(DMPlexSetConeSize(*dm, c, 3));
6978   PetscCall(DMSetUp(*dm));
6979   for (PetscInt c = 0; c < Nc; ++c) PetscCall(DMPlexSetCone(*dm, c, &cells[c * 3]));
6980   PetscCall(PetscFree(cells));
6981   PetscCall(DMPlexSymmetrize(*dm));
6982   PetscCall(DMPlexStratify(*dm));
6983 
6984   PetscCall(DMGetCoordinateSection(*dm, &coordSection));
6985   PetscCall(PetscSectionSetNumFields(coordSection, 1));
6986   PetscCall(PetscSectionSetFieldComponents(coordSection, 0, 3));
6987   PetscCall(PetscSectionSetChart(coordSection, Nc, Nc + Nv));
6988   for (PetscInt v = Nc; v < Nc + Nv; ++v) {
6989     PetscCall(PetscSectionSetDof(coordSection, v, 3));
6990     PetscCall(PetscSectionSetFieldDof(coordSection, v, 0, 3));
6991   }
6992   PetscCall(PetscSectionSetUp(coordSection));
6993   PetscCall(PetscSectionGetStorageSize(coordSection, &coordSize));
6994   PetscCall(VecCreate(PETSC_COMM_SELF, &coordinates));
6995   PetscCall(PetscObjectSetName((PetscObject)coordinates, "coordinates"));
6996   PetscCall(VecSetSizes(coordinates, coordSize, PETSC_DETERMINE));
6997   PetscCall(VecSetBlockSize(coordinates, 3));
6998   PetscCall(VecSetType(coordinates, VECSTANDARD));
6999   PetscCall(VecGetArray(coordinates, &coords));
7000   for (PetscInt tv = 0; tv < Nc * 3; ++tv) {
7001     if (PetscBTLookup(bt, tv)) {
7002       for (PetscInt d = 0; d < 3; ++d) coords[nv * 3 + d] = trialCoords[tv * 3 + d];
7003       ++nv;
7004     }
7005   }
7006   PetscCheck(nv == Nv, comm, PETSC_ERR_PLIB, "Number of vertices copied %" PetscInt_FMT " != %" PetscInt_FMT " nubmer of mesh vertices", nv, Nv);
7007   PetscCall(PetscFree(trialCoords));
7008   PetscCall(PetscBTDestroy(&bt));
7009   PetscCall(VecRestoreArray(coordinates, &coords));
7010   PetscCall(DMSetCoordinatesLocal(*dm, coordinates));
7011   PetscCall(VecDestroy(&coordinates));
7012   PetscCall(PetscViewerDestroy(&viewer));
7013   if (interpolate) {
7014     DM idm;
7015 
7016     PetscCall(DMPlexInterpolate(*dm, &idm));
7017     PetscCall(DMDestroy(dm));
7018     *dm = idm;
7019   }
7020   PetscFunctionReturn(PETSC_SUCCESS);
7021 }
7022 
7023 /*
7024   DMPlexCreateShapefile - Create a `DMPLEX` mesh from the ESRI Shapefile format.
7025 
7026   Collective
7027 
7028 + comm   - The MPI communicator
7029 - viewer - `PetscViewer` for the .shp file
7030 
7031   Output Parameter:
7032 . dm - The `DM` object representing the mesh
7033 
7034   Level: beginner
7035 
7036   Note:
7037   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).
7038 
7039 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateFromFile()`, `DMPlexCreateGmsh()`, `DMPlexCreate()`
7040 */
7041 static PetscErrorCode DMPlexCreateShapefile(MPI_Comm comm, PetscViewer viewer, DM *dm)
7042 {
7043   Vec          coordinates;
7044   PetscSection coordSection;
7045   PetscScalar *coords;
7046   PetscInt     cdim   = 2, coordSize;
7047   int          dim    = 1, Nv, Nc, vOff;
7048   double      *points = NULL;
7049   double       mbr[4], zb[2], mb[2];
7050   PetscMPIInt  rank;
7051 
7052   PetscFunctionBegin;
7053   PetscCallMPI(MPI_Comm_rank(comm, &rank));
7054   if (rank == 0) {
7055     PetscInt cnt;
7056     int      magic, dummy[5], len, version, shtype, totlen = 0;
7057 
7058     // Read header
7059     PetscCall(PetscViewerBinaryRead(viewer, &magic, 1, &cnt, PETSC_INT32));
7060     PetscCheck(cnt == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unable to parse Shapefile file: could not read bytes 0-3");
7061     PetscCheck(magic == 0x0000270a, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unable to parse Shapefile file: invalid magic number %X", magic);
7062     PetscCall(PetscViewerBinaryRead(viewer, dummy, 5, &cnt, PETSC_INT32));
7063     PetscCheck(cnt == 5, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unable to parse Shapefile file: could not read bytes 4-23");
7064     PetscCall(PetscViewerBinaryRead(viewer, &len, 1, &cnt, PETSC_INT32));
7065     PetscCheck(cnt == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unable to parse Shapefile file: could not read bytes 24-27");
7066     PetscCall(PetscViewerBinaryRead(viewer, &version, 1, &cnt, PETSC_INT32));
7067     PetscCall(PetscByteSwap(&version, PETSC_INT32, 1));
7068     PetscCheck(cnt == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unable to parse Shapefile file: could not read bytes 28-31");
7069     PetscCall(PetscInfo(viewer, "Shapefile: version %d file length %d\n", version, len));
7070     PetscCall(PetscViewerBinaryRead(viewer, &shtype, 1, &cnt, PETSC_INT32));
7071     PetscCall(PetscByteSwap(&shtype, PETSC_INT32, 1));
7072     PetscCheck(cnt == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unable to parse Shapefile file: could not read bytes 32-35");
7073     PetscCall(PetscInfo(viewer, "Shapefile: shape type %d\n", shtype));
7074     PetscCall(PetscViewerBinaryRead(viewer, mbr, 4, &cnt, PETSC_DOUBLE));
7075     PetscCall(PetscByteSwap(mbr, PETSC_DOUBLE, 4));
7076     PetscCheck(cnt == 4, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unable to parse Shapefile file: could not read bytes 36-67");
7077     PetscCall(PetscInfo(viewer, "Shapefile: minimum bounding rectangle (%g, %g) -- (%g, %g)\n", mbr[0], mbr[1], mbr[2], mbr[3]));
7078     PetscCall(PetscViewerBinaryRead(viewer, zb, 2, &cnt, PETSC_DOUBLE));
7079     PetscCall(PetscByteSwap(zb, PETSC_DOUBLE, 2));
7080     PetscCheck(cnt == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unable to parse Shapefile file: could not read bytes 68-83");
7081     PetscCall(PetscInfo(viewer, "Shapefile: Z bounds (%g) -- (%g)\n", zb[0], zb[1]));
7082     PetscCall(PetscViewerBinaryRead(viewer, mb, 2, &cnt, PETSC_DOUBLE));
7083     PetscCall(PetscByteSwap(mb, PETSC_DOUBLE, 2));
7084     PetscCheck(cnt == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unable to parse Shapefile file: could not read bytes 84-99");
7085     PetscCall(PetscInfo(viewer, "Shapefile: M bounds (%g) -- (%g)\n", mb[0], mb[1]));
7086     totlen += 100;
7087     {
7088       int    rnum, rlen, rshtype, rnpart, rnp;
7089       double rmbr[4];
7090       int   *partOffsets;
7091 
7092       // Read record header
7093       PetscCall(PetscViewerBinaryRead(viewer, &rnum, 1, &cnt, PETSC_INT32));
7094       PetscCheck(cnt == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unable to parse Shapefile file: could not read record header bytes 0-3");
7095       PetscCall(PetscViewerBinaryRead(viewer, &rlen, 1, &cnt, PETSC_INT32));
7096       PetscCheck(cnt == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unable to parse Shapefile file: could not read record header bytes 4-7");
7097       PetscCall(PetscInfo(viewer, "Shapefile: record %d length %d\n", rnum, rlen));
7098       totlen += 8;
7099       // Read record
7100       PetscCall(PetscViewerBinaryRead(viewer, &rshtype, 1, &cnt, PETSC_INT32));
7101       PetscCall(PetscByteSwap(&rshtype, PETSC_INT32, 1));
7102       PetscCheck(cnt == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unable to parse Shapefile file: could not read record bytes 0-3");
7103       PetscCall(PetscInfo(viewer, "Shapefile: record shape type %d\n", rshtype));
7104       totlen += 4;
7105       switch (rshtype) {
7106       case 5: // Polygon
7107         PetscCall(PetscViewerBinaryRead(viewer, rmbr, 4, &cnt, PETSC_DOUBLE));
7108         PetscCall(PetscByteSwap(rmbr, PETSC_DOUBLE, 4));
7109         PetscCheck(cnt == 4, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unable to parse Shapefile file: could not read record bytes 4-35");
7110         PetscCall(PetscInfo(viewer, "Shapefile: record minimum bounding rectangle (%g, %g) -- (%g, %g)\n", rmbr[0], rmbr[1], rmbr[2], rmbr[3]));
7111         PetscCall(PetscViewerBinaryRead(viewer, &rnpart, 1, &cnt, PETSC_INT32));
7112         PetscCall(PetscByteSwap(&rnpart, PETSC_INT32, 1));
7113         PetscCheck(cnt == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unable to parse Shapefile file: could not read record bytes 36-39");
7114         PetscCall(PetscInfo(viewer, "Shapefile: record shape number of parts %d\n", rnpart));
7115         PetscCall(PetscViewerBinaryRead(viewer, &rnp, 1, &cnt, PETSC_INT32));
7116         PetscCall(PetscByteSwap(&rnp, PETSC_INT32, 1));
7117         PetscCheck(cnt == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unable to parse Shapefile file: could not read record bytes 36-39");
7118         PetscCall(PetscInfo(viewer, "Shapefile: record shape number of points %d\n", rnp));
7119         totlen += 40;
7120         PetscCall(PetscMalloc1(rnpart, &partOffsets));
7121         PetscCall(PetscViewerBinaryRead(viewer, partOffsets, rnpart, &cnt, PETSC_INT32));
7122         PetscCall(PetscByteSwap(partOffsets, PETSC_INT32, rnpart));
7123         PetscCheck(cnt == rnpart, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unable to parse Shapefile file: could not read record part offsets");
7124         totlen += 4 * rnpart;
7125         PetscCall(PetscMalloc1(rnp * 2, &points));
7126         PetscCall(PetscViewerBinaryRead(viewer, points, rnp * 2, &cnt, PETSC_DOUBLE));
7127         PetscCall(PetscByteSwap(points, PETSC_DOUBLE, rnp * 2));
7128         PetscCheck(cnt == rnp * 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unable to parse Shapefile file: could not read record points");
7129         totlen += 8 * rnp * 2;
7130         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);
7131         // Only get the last polygon now
7132         vOff = partOffsets[rnpart - 1];
7133         Nv   = rnp - vOff;
7134         Nc   = Nv - 1;
7135         PetscCall(PetscInfo(viewer, "Shapefile: record first polygon size %d totlen %d\n", Nv, totlen));
7136         PetscCall(PetscFree(partOffsets));
7137         break;
7138       default:
7139         PetscCheck(0, PETSC_COMM_SELF, PETSC_ERR_SUP, "Only support polygons right now");
7140       }
7141     }
7142   } else {
7143     Nc = Nv = vOff = 0;
7144   }
7145   PetscCallMPI(MPI_Bcast(&dim, 1, MPI_INT, 0, comm));
7146   PetscCall(DMCreate(comm, dm));
7147   PetscCall(DMSetType(*dm, DMPLEX));
7148   PetscCall(DMPlexSetChart(*dm, 0, Nc + Nv));
7149   PetscCall(DMSetDimension(*dm, dim));
7150   PetscCall(DMSetCoordinateDim(*dm, cdim));
7151   // Topology of a circle
7152   if (rank == 0) {
7153     for (PetscInt c = 0; c < Nc; ++c) PetscCall(DMPlexSetConeSize(*dm, c, 2));
7154     PetscCall(DMSetUp(*dm));
7155     for (PetscInt c = 0; c < Nc; ++c) {
7156       PetscInt cone[2] = {Nc + c, Nc + c + 1};
7157 
7158       PetscCall(DMPlexSetCone(*dm, c, cone));
7159     }
7160   }
7161   PetscCall(DMPlexSymmetrize(*dm));
7162   PetscCall(DMPlexStratify(*dm));
7163   // Set coordinates
7164   PetscCall(DMGetCoordinateSection(*dm, &coordSection));
7165   PetscCall(PetscSectionSetNumFields(coordSection, 1));
7166   PetscCall(PetscSectionSetFieldComponents(coordSection, 0, cdim));
7167   PetscCall(PetscSectionSetChart(coordSection, Nc, Nc + Nv));
7168   for (PetscInt v = Nc; v < Nc + Nv; ++v) {
7169     PetscCall(PetscSectionSetDof(coordSection, v, cdim));
7170     PetscCall(PetscSectionSetFieldDof(coordSection, v, 0, cdim));
7171   }
7172   PetscCall(PetscSectionSetUp(coordSection));
7173   PetscCall(PetscSectionGetStorageSize(coordSection, &coordSize));
7174   PetscCall(VecCreate(PETSC_COMM_SELF, &coordinates));
7175   PetscCall(PetscObjectSetName((PetscObject)coordinates, "coordinates"));
7176   PetscCall(VecSetSizes(coordinates, coordSize, PETSC_DETERMINE));
7177   PetscCall(VecSetBlockSize(coordinates, cdim));
7178   PetscCall(VecSetType(coordinates, VECSTANDARD));
7179   PetscCall(VecGetArray(coordinates, &coords));
7180   for (PetscInt v = 0; v < Nv; ++v) {
7181     coords[v * cdim + 0] = points[(v + vOff) * cdim + 0] - mbr[0];
7182     coords[v * cdim + 1] = points[(v + vOff) * cdim + 1] - mbr[1];
7183   }
7184   PetscCall(PetscFree(points));
7185   PetscCall(VecRestoreArray(coordinates, &coords));
7186   PetscCall(DMSetCoordinatesLocal(*dm, coordinates));
7187   PetscCall(VecDestroy(&coordinates));
7188   PetscFunctionReturn(PETSC_SUCCESS);
7189 }
7190 
7191 /*
7192   DMPlexCreateShapefileFromFile - Create a `DMPLEX` mesh from the ESRI Shapefile format.
7193 
7194   Collective
7195 
7196 + comm     - The MPI communicator
7197 . filename - Name of the .shp file
7198 
7199   Output Parameter:
7200 . dm - The `DM` object representing the mesh
7201 
7202   Level: beginner
7203 
7204   Note:
7205   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).
7206 
7207 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateFromFile()`, `DMPlexCreateGmsh()`, `DMPlexCreate()`
7208 */
7209 static PetscErrorCode DMPlexCreateShapefileFromFile(MPI_Comm comm, const char filename[], DM *dm)
7210 {
7211   PetscViewer viewer;
7212 
7213   PetscFunctionBegin;
7214   PetscCall(PetscViewerCreate(comm, &viewer));
7215   PetscCall(PetscViewerSetType(viewer, PETSCVIEWERBINARY));
7216   PetscCall(PetscViewerFileSetMode(viewer, FILE_MODE_READ));
7217   PetscCall(PetscViewerFileSetName(viewer, filename));
7218   PetscCall(DMPlexCreateShapefile(comm, viewer, dm));
7219   PetscCall(PetscViewerDestroy(&viewer));
7220   PetscFunctionReturn(PETSC_SUCCESS);
7221 }
7222 
7223 /*@
7224   DMPlexCreateFromFile - This takes a filename and produces a `DM`
7225 
7226   Collective
7227 
7228   Input Parameters:
7229 + comm        - The communicator
7230 . filename    - A file name
7231 . plexname    - The object name of the resulting `DM`, also used for intra-datafile lookup by some formats
7232 - interpolate - Flag to create intermediate mesh pieces (edges, faces)
7233 
7234   Output Parameter:
7235 . dm - The `DM`
7236 
7237   Options Database Key:
7238 . -dm_plex_create_from_hdf5_xdmf - use the `PETSC_VIEWER_HDF5_XDMF` format for reading HDF5
7239 
7240   Use `-dm_plex_create_ prefix` to pass options to the internal `PetscViewer`, e.g. `-dm_plex_create_viewer_hdf5_collective`
7241 
7242   Level: beginner
7243 
7244   Notes:
7245   Using `PETSCVIEWERHDF5` type with `PETSC_VIEWER_HDF5_PETSC` format, one can save multiple `DMPLEX`
7246   meshes in a single HDF5 file. This in turn requires one to name the `DMPLEX` object with `PetscObjectSetName()`
7247   before saving it with `DMView()` and before loading it with `DMLoad()` for identification of the mesh object.
7248   The input parameter name is thus used to name the `DMPLEX` object when `DMPlexCreateFromFile()` internally
7249   calls `DMLoad()`. Currently, name is ignored for other viewer types and/or formats.
7250 
7251 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateFromDAG()`, `DMPlexCreateFromCellListPetsc()`, `DMPlexCreate()`, `PetscObjectSetName()`, `DMView()`, `DMLoad()`
7252 @*/
7253 PetscErrorCode DMPlexCreateFromFile(MPI_Comm comm, const char filename[], const char plexname[], PetscBool interpolate, DM *dm)
7254 {
7255   const char  extGmsh[]      = ".msh";
7256   const char  extGmsh2[]     = ".msh2";
7257   const char  extGmsh4[]     = ".msh4";
7258   const char  extCGNS[]      = ".cgns";
7259   const char  extExodus[]    = ".exo";
7260   const char  extExodus_e[]  = ".e";
7261   const char  extGenesis[]   = ".gen";
7262   const char  extFluent[]    = ".cas";
7263   const char  extHDF5[]      = ".h5";
7264   const char  extXDMFHDF5[]  = ".xdmf.h5";
7265   const char  extPLY[]       = ".ply";
7266   const char  extEGADSlite[] = ".egadslite";
7267   const char  extEGADS[]     = ".egads";
7268   const char  extIGES[]      = ".igs";
7269   const char  extIGES2[]     = ".iges";
7270   const char  extSTEP[]      = ".stp";
7271   const char  extSTEP2[]     = ".step";
7272   const char  extBREP[]      = ".brep";
7273   const char  extCV[]        = ".dat";
7274   const char  extSTL[]       = ".stl";
7275   const char  extSHP[]       = ".shp";
7276   size_t      len;
7277   PetscBool   isGmsh, isGmsh2, isGmsh4, isCGNS, isExodus, isGenesis, isFluent, isHDF5, isPLY, isEGADSlite, isEGADS, isIGES, isIGES2, isSTEP, isSTEP2, isBREP, isCV, isSTL, isSHP, isXDMFHDF5;
7278   PetscMPIInt rank;
7279 
7280   PetscFunctionBegin;
7281   PetscAssertPointer(filename, 2);
7282   if (plexname) PetscAssertPointer(plexname, 3);
7283   PetscAssertPointer(dm, 5);
7284   PetscCall(DMInitializePackage());
7285   PetscCall(PetscLogEventBegin(DMPLEX_CreateFromFile, 0, 0, 0, 0));
7286   PetscCallMPI(MPI_Comm_rank(comm, &rank));
7287   PetscCall(PetscStrlen(filename, &len));
7288   PetscCheck(len, comm, PETSC_ERR_ARG_WRONG, "Filename must be a valid path");
7289 
7290 #define CheckExtension(extension__, is_extension__) \
7291   do { \
7292     PetscAssert(sizeof(extension__), comm, PETSC_ERR_PLIB, "Zero-size extension: %s", extension__); \
7293     /* don't count the null-terminator at the end */ \
7294     const size_t ext_len = sizeof(extension__) - 1; \
7295     if (len < ext_len) { \
7296       is_extension__ = PETSC_FALSE; \
7297     } else { \
7298       PetscCall(PetscStrncmp(filename + len - ext_len, extension__, ext_len, &is_extension__)); \
7299     } \
7300   } while (0)
7301 
7302   CheckExtension(extGmsh, isGmsh);
7303   CheckExtension(extGmsh2, isGmsh2);
7304   CheckExtension(extGmsh4, isGmsh4);
7305   CheckExtension(extCGNS, isCGNS);
7306   CheckExtension(extExodus, isExodus);
7307   if (!isExodus) CheckExtension(extExodus_e, isExodus);
7308   CheckExtension(extGenesis, isGenesis);
7309   CheckExtension(extFluent, isFluent);
7310   CheckExtension(extHDF5, isHDF5);
7311   CheckExtension(extPLY, isPLY);
7312   CheckExtension(extEGADSlite, isEGADSlite);
7313   CheckExtension(extEGADS, isEGADS);
7314   CheckExtension(extIGES, isIGES);
7315   CheckExtension(extIGES2, isIGES2);
7316   CheckExtension(extSTEP, isSTEP);
7317   CheckExtension(extSTEP2, isSTEP2);
7318   CheckExtension(extBREP, isBREP);
7319   CheckExtension(extCV, isCV);
7320   CheckExtension(extSTL, isSTL);
7321   CheckExtension(extSHP, isSHP);
7322   CheckExtension(extXDMFHDF5, isXDMFHDF5);
7323 
7324 #undef CheckExtension
7325 
7326   if (isGmsh || isGmsh2 || isGmsh4) {
7327     PetscCall(DMPlexCreateGmshFromFile(comm, filename, interpolate, dm));
7328   } else if (isCGNS) {
7329     PetscCall(DMPlexCreateCGNSFromFile(comm, filename, interpolate, dm));
7330   } else if (isExodus || isGenesis) {
7331     PetscCall(DMPlexCreateExodusFromFile(comm, filename, interpolate, dm));
7332   } else if (isFluent) {
7333     PetscCall(DMPlexCreateFluentFromFile(comm, filename, interpolate, dm));
7334   } else if (isHDF5) {
7335     PetscViewer viewer;
7336 
7337     /* PETSC_VIEWER_HDF5_XDMF is used if the filename ends with .xdmf.h5, or if -dm_plex_create_from_hdf5_xdmf option is present */
7338     PetscCall(PetscOptionsGetBool(NULL, NULL, "-dm_plex_create_from_hdf5_xdmf", &isXDMFHDF5, NULL));
7339     PetscCall(PetscViewerCreate(comm, &viewer));
7340     PetscCall(PetscViewerSetType(viewer, PETSCVIEWERHDF5));
7341     PetscCall(PetscViewerSetOptionsPrefix(viewer, "dm_plex_create_"));
7342     PetscCall(PetscViewerSetFromOptions(viewer));
7343     PetscCall(PetscViewerFileSetMode(viewer, FILE_MODE_READ));
7344     PetscCall(PetscViewerFileSetName(viewer, filename));
7345 
7346     PetscCall(DMCreate(comm, dm));
7347     PetscCall(PetscObjectSetName((PetscObject)*dm, plexname));
7348     PetscCall(DMSetType(*dm, DMPLEX));
7349     if (isXDMFHDF5) PetscCall(PetscViewerPushFormat(viewer, PETSC_VIEWER_HDF5_XDMF));
7350     PetscCall(DMLoad(*dm, viewer));
7351     if (isXDMFHDF5) PetscCall(PetscViewerPopFormat(viewer));
7352     PetscCall(PetscViewerDestroy(&viewer));
7353 
7354     if (interpolate) {
7355       DM idm;
7356 
7357       PetscCall(DMPlexInterpolate(*dm, &idm));
7358       PetscCall(DMDestroy(dm));
7359       *dm = idm;
7360     }
7361   } else if (isPLY) {
7362     PetscCall(DMPlexCreatePLYFromFile(comm, filename, interpolate, dm));
7363   } else if (isEGADSlite || isEGADS || isIGES || isIGES2 || isSTEP || isSTEP2 || isBREP) {
7364     PetscCall(DMPlexCreateGeomFromFile(comm, filename, dm, isEGADSlite));
7365 
7366     if (!interpolate) {
7367       DM udm;
7368 
7369       PetscCall(DMPlexUninterpolate(*dm, &udm));
7370       PetscCall(DMDestroy(dm));
7371       *dm = udm;
7372     }
7373   } else if (isCV) {
7374     PetscCall(DMPlexCreateCellVertexFromFile(comm, filename, interpolate, dm));
7375   } else if (isSTL) {
7376     PetscCall(DMPlexCreateSTLFromFile(comm, filename, interpolate, dm));
7377   } else if (isSHP) {
7378     PetscCall(DMPlexCreateShapefileFromFile(comm, filename, dm));
7379   } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cannot load file %s: unrecognized extension", filename);
7380   PetscCall(PetscStrlen(plexname, &len));
7381   if (len) PetscCall(PetscObjectSetName((PetscObject)*dm, plexname));
7382   PetscCall(PetscLogEventEnd(DMPLEX_CreateFromFile, 0, 0, 0, 0));
7383   PetscFunctionReturn(PETSC_SUCCESS);
7384 }
7385 
7386 /*@
7387   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.
7388 
7389   Input Parameters:
7390 + tr     - The `DMPlexTransform`
7391 - prefix - An options prefix, or NULL
7392 
7393   Output Parameter:
7394 . dm - The `DM`
7395 
7396   Level: beginner
7397 
7398   Notes:
7399   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.
7400 
7401 .seealso: `DMPlexCreateFromFile`, `DMPlexCreateFromDAG()`, `DMPlexCreateFromCellListPetsc()`, `DMPlexCreate()`
7402 @*/
7403 PetscErrorCode DMPlexCreateEphemeral(DMPlexTransform tr, const char prefix[], DM *dm)
7404 {
7405   DM           bdm, bcdm, cdm;
7406   Vec          coordinates, coordinatesNew;
7407   PetscSection cs;
7408   PetscInt     cdim, Nl;
7409 
7410   PetscFunctionBegin;
7411   PetscCall(DMCreate(PetscObjectComm((PetscObject)tr), dm));
7412   PetscCall(DMSetType(*dm, DMPLEX));
7413   ((DM_Plex *)(*dm)->data)->interpolated = DMPLEX_INTERPOLATED_FULL;
7414   // Handle coordinates
7415   PetscCall(DMPlexTransformGetDM(tr, &bdm));
7416   PetscCall(DMPlexTransformSetDimensions(tr, bdm, *dm));
7417   PetscCall(DMGetCoordinateDim(*dm, &cdim));
7418   PetscCall(DMGetCoordinateDM(bdm, &bcdm));
7419   PetscCall(DMGetCoordinateDM(*dm, &cdm));
7420   PetscCall(DMCopyDisc(bcdm, cdm));
7421   PetscCall(DMGetLocalSection(cdm, &cs));
7422   PetscCall(PetscSectionSetNumFields(cs, 1));
7423   PetscCall(PetscSectionSetFieldComponents(cs, 0, cdim));
7424   PetscCall(DMGetCoordinatesLocal(bdm, &coordinates));
7425   PetscCall(VecDuplicate(coordinates, &coordinatesNew));
7426   PetscCall(VecCopy(coordinates, coordinatesNew));
7427   PetscCall(DMSetCoordinatesLocal(*dm, coordinatesNew));
7428   PetscCall(VecDestroy(&coordinatesNew));
7429 
7430   PetscCall(PetscObjectReference((PetscObject)tr));
7431   PetscCall(DMPlexTransformDestroy(&((DM_Plex *)(*dm)->data)->tr));
7432   ((DM_Plex *)(*dm)->data)->tr = tr;
7433   PetscCall(DMPlexDistributeSetDefault(*dm, PETSC_FALSE));
7434   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*dm, prefix));
7435   PetscCall(DMSetFromOptions(*dm));
7436 
7437   PetscCall(DMGetNumLabels(bdm, &Nl));
7438   for (PetscInt l = 0; l < Nl; ++l) {
7439     DMLabel     label, labelNew;
7440     const char *lname;
7441     PetscBool   isDepth, isCellType;
7442 
7443     PetscCall(DMGetLabelName(bdm, l, &lname));
7444     PetscCall(PetscStrcmp(lname, "depth", &isDepth));
7445     if (isDepth) continue;
7446     PetscCall(PetscStrcmp(lname, "celltype", &isCellType));
7447     if (isCellType) continue;
7448     PetscCall(DMCreateLabel(*dm, lname));
7449     PetscCall(DMGetLabel(bdm, lname, &label));
7450     PetscCall(DMGetLabel(*dm, lname, &labelNew));
7451     PetscCall(DMLabelSetType(labelNew, DMLABELEPHEMERAL));
7452     PetscCall(DMLabelEphemeralSetLabel(labelNew, label));
7453     PetscCall(DMLabelEphemeralSetTransform(labelNew, tr));
7454     PetscCall(DMLabelSetUp(labelNew));
7455   }
7456   PetscFunctionReturn(PETSC_SUCCESS);
7457 }
7458