xref: /petsc/src/dm/impls/plex/plex.c (revision 294d477ba62cbf5d5a9b4f54ad5a37bc60d501cc)
1 #include <petsc/private/dmpleximpl.h> /*I      "petscdmplex.h"   I*/
2 #include <petsc/private/dmlabelimpl.h>
3 #include <petsc/private/isimpl.h>
4 #include <petsc/private/vecimpl.h>
5 #include <petsc/private/glvisvecimpl.h>
6 #include <petscsf.h>
7 #include <petscds.h>
8 #include <petscdraw.h>
9 #include <petscdmfield.h>
10 #include <petscdmplextransform.h>
11 
12 /* Logging support */
13 PetscLogEvent DMPLEX_Interpolate, DMPLEX_Partition, DMPLEX_Distribute, DMPLEX_DistributeCones, DMPLEX_DistributeLabels, DMPLEX_DistributeSF, DMPLEX_DistributeOverlap, DMPLEX_DistributeField, DMPLEX_DistributeData, DMPLEX_Migrate, DMPLEX_InterpolateSF, DMPLEX_GlobalToNaturalBegin, DMPLEX_GlobalToNaturalEnd, DMPLEX_NaturalToGlobalBegin, DMPLEX_NaturalToGlobalEnd, DMPLEX_Stratify, DMPLEX_Symmetrize, DMPLEX_Preallocate, DMPLEX_ResidualFEM, DMPLEX_JacobianFEM, DMPLEX_InterpolatorFEM, DMPLEX_InjectorFEM, DMPLEX_IntegralFEM, DMPLEX_CreateGmsh, DMPLEX_RebalanceSharedPoints, DMPLEX_PartSelf, DMPLEX_PartLabelInvert, DMPLEX_PartLabelCreateSF, DMPLEX_PartStratSF, DMPLEX_CreatePointSF, DMPLEX_LocatePoints, DMPLEX_TopologyView, DMPLEX_LabelsView, DMPLEX_CoordinatesView, DMPLEX_SectionView, DMPLEX_GlobalVectorView, DMPLEX_LocalVectorView, DMPLEX_TopologyLoad, DMPLEX_LabelsLoad, DMPLEX_CoordinatesLoad, DMPLEX_SectionLoad, DMPLEX_GlobalVectorLoad, DMPLEX_LocalVectorLoad;
14 PetscLogEvent DMPLEX_RebalBuildGraph, DMPLEX_RebalRewriteSF, DMPLEX_RebalGatherGraph, DMPLEX_RebalPartition, DMPLEX_RebalScatterPart, DMPLEX_Generate, DMPLEX_Transform, DMPLEX_GetLocalOffsets, DMPLEX_Uninterpolate;
15 
16 PetscBool  Plexcite       = PETSC_FALSE;
17 const char PlexCitation[] = "@article{LangeMitchellKnepleyGorman2015,\n"
18                             "title     = {Efficient mesh management in {Firedrake} using {PETSc-DMPlex}},\n"
19                             "author    = {Michael Lange and Lawrence Mitchell and Matthew G. Knepley and Gerard J. Gorman},\n"
20                             "journal   = {SIAM Journal on Scientific Computing},\n"
21                             "volume    = {38},\n"
22                             "number    = {5},\n"
23                             "pages     = {S143--S155},\n"
24                             "eprint    = {http://arxiv.org/abs/1506.07749},\n"
25                             "doi       = {10.1137/15M1026092},\n"
26                             "year      = {2016},\n"
27                             "petsc_uses={DMPlex},\n}\n";
28 
29 PETSC_EXTERN PetscErrorCode VecView_MPI(Vec, PetscViewer);
30 
31 /*@
32   DMPlexIsSimplex - Is the first cell in this mesh a simplex?
33 
34   Input Parameter:
35 . dm - The `DMPLEX` object
36 
37   Output Parameter:
38 . simplex - Flag checking for a simplex
39 
40   Level: intermediate
41 
42   Note:
43   This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
44   If the mesh has no cells, this returns `PETSC_FALSE`.
45 
46 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSimplexOrBoxCells()`, `DMPlexGetCellType()`, `DMPlexGetHeightStratum()`, `DMPolytopeTypeGetNumVertices()`
47 @*/
48 PetscErrorCode DMPlexIsSimplex(DM dm, PetscBool *simplex)
49 {
50   DMPolytopeType ct;
51   PetscInt       cStart, cEnd;
52 
53   PetscFunctionBegin;
54   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
55   if (cEnd <= cStart) {
56     *simplex = PETSC_FALSE;
57     PetscFunctionReturn(PETSC_SUCCESS);
58   }
59   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
60   *simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
61   PetscFunctionReturn(PETSC_SUCCESS);
62 }
63 
64 /*@
65   DMPlexGetSimplexOrBoxCells - Get the range of cells which are neither prisms nor ghost FV cells
66 
67   Input Parameters:
68 + dm     - The `DMPLEX` object
69 - height - The cell height in the Plex, 0 is the default
70 
71   Output Parameters:
72 + cStart - The first "normal" cell
73 - cEnd   - The upper bound on "normal"" cells
74 
75   Level: developer
76 
77   Note:
78   This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
79 
80 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetCellTypeStratum()`
81 @*/
82 PetscErrorCode DMPlexGetSimplexOrBoxCells(DM dm, PetscInt height, PetscInt *cStart, PetscInt *cEnd)
83 {
84   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
85   PetscInt       cS, cE, c;
86 
87   PetscFunctionBegin;
88   PetscCall(DMPlexGetHeightStratum(dm, PetscMax(height, 0), &cS, &cE));
89   for (c = cS; c < cE; ++c) {
90     DMPolytopeType cct;
91 
92     PetscCall(DMPlexGetCellType(dm, c, &cct));
93     if ((PetscInt)cct < 0) break;
94     switch (cct) {
95     case DM_POLYTOPE_POINT:
96     case DM_POLYTOPE_SEGMENT:
97     case DM_POLYTOPE_TRIANGLE:
98     case DM_POLYTOPE_QUADRILATERAL:
99     case DM_POLYTOPE_TETRAHEDRON:
100     case DM_POLYTOPE_HEXAHEDRON:
101       ct = cct;
102       break;
103     default:
104       break;
105     }
106     if (ct != DM_POLYTOPE_UNKNOWN) break;
107   }
108   if (ct != DM_POLYTOPE_UNKNOWN) {
109     DMLabel ctLabel;
110 
111     PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
112     PetscCall(DMLabelGetStratumBounds(ctLabel, ct, &cS, &cE));
113     // Reset label for fast lookup
114     PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
115   }
116   if (cStart) *cStart = cS;
117   if (cEnd) *cEnd = cE;
118   PetscFunctionReturn(PETSC_SUCCESS);
119 }
120 
121 PetscErrorCode DMPlexGetFieldTypes_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *types, PetscInt **ssStart, PetscInt **ssEnd, PetscViewerVTKFieldType **sft)
122 {
123   PetscInt                 cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd, c, depth, cellHeight, t;
124   PetscInt                *sStart, *sEnd;
125   PetscViewerVTKFieldType *ft;
126   PetscInt                 vcdof[DM_NUM_POLYTOPES + 1], globalvcdof[DM_NUM_POLYTOPES + 1];
127   DMLabel                  depthLabel, ctLabel;
128 
129   PetscFunctionBegin;
130 
131   /* the vcdof and globalvcdof are sized to allow every polytope type and simple vertex at DM_NUM_POLYTOPES */
132   PetscCall(PetscArrayzero(vcdof, DM_NUM_POLYTOPES + 1));
133   PetscCall(DMGetCoordinateDim(dm, &cdim));
134   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
135   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
136   if (field >= 0) {
137     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[DM_NUM_POLYTOPES]));
138   } else {
139     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[DM_NUM_POLYTOPES]));
140   }
141 
142   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
143   PetscCall(DMPlexGetDepth(dm, &depth));
144   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
145   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
146   for (c = 0; c < DM_NUM_POLYTOPES; ++c) {
147     const DMPolytopeType ict = (DMPolytopeType)c;
148     PetscInt             dep;
149 
150     if (ict == DM_POLYTOPE_FV_GHOST) continue;
151     PetscCall(DMLabelGetStratumBounds(ctLabel, ict, &cStart, &cEnd));
152     if (pStart >= 0) {
153       PetscCall(DMLabelGetValue(depthLabel, cStart, &dep));
154       if (dep != depth - cellHeight) continue;
155     }
156     if (field >= 0) {
157       if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[c]));
158     } else {
159       if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[c]));
160     }
161     PetscCall(MPIU_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
162   }
163 
164   PetscCall(MPIU_Allreduce(vcdof, globalvcdof, DM_NUM_POLYTOPES + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
165   *types = 0;
166 
167   for (c = 0; c < DM_NUM_POLYTOPES + 1; ++c) {
168     if (globalvcdof[c]) ++(*types);
169   }
170 
171   PetscCall(PetscMalloc3(*types, &sStart, *types, &sEnd, *types, &ft));
172   t = 0;
173   if (globalvcdof[DM_NUM_POLYTOPES]) {
174     sStart[t] = vStart;
175     sEnd[t]   = vEnd;
176     ft[t]     = (globalvcdof[t] == cdim) ? PETSC_VTK_POINT_VECTOR_FIELD : PETSC_VTK_POINT_FIELD;
177     ++t;
178   }
179 
180   for (c = 0; c < DM_NUM_POLYTOPES; ++c) {
181     if (globalvcdof[c]) {
182       const DMPolytopeType ict = (DMPolytopeType)c;
183 
184       PetscCall(DMLabelGetStratumBounds(ctLabel, ict, &cStart, &cEnd));
185       sStart[t] = cStart;
186       sEnd[t]   = cEnd;
187       ft[t]     = (globalvcdof[c] == cdim) ? PETSC_VTK_CELL_VECTOR_FIELD : PETSC_VTK_CELL_FIELD;
188       ++t;
189     }
190   }
191 
192   if (!(*types)) {
193     if (field >= 0) {
194       const char *fieldname;
195 
196       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
197       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
198     } else {
199       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
200     }
201   }
202 
203   *ssStart = sStart;
204   *ssEnd   = sEnd;
205   *sft     = ft;
206   PetscFunctionReturn(PETSC_SUCCESS);
207 }
208 
209 PetscErrorCode DMPlexRestoreFieldTypes_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *types, PetscInt **sStart, PetscInt **sEnd, PetscViewerVTKFieldType **ft)
210 {
211   PetscFunctionBegin;
212   PetscCall(PetscFree3(*sStart, *sEnd, *ft));
213   PetscFunctionReturn(PETSC_SUCCESS);
214 }
215 
216 PetscErrorCode DMPlexGetFieldType_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *sStart, PetscInt *sEnd, PetscViewerVTKFieldType *ft)
217 {
218   PetscInt cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd;
219   PetscInt vcdof[2] = {0, 0}, globalvcdof[2];
220 
221   PetscFunctionBegin;
222   *ft = PETSC_VTK_INVALID;
223   PetscCall(DMGetCoordinateDim(dm, &cdim));
224   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
225   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
226   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
227   if (field >= 0) {
228     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[0]));
229     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[1]));
230   } else {
231     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[0]));
232     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[1]));
233   }
234   PetscCall(MPIU_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
235   if (globalvcdof[0]) {
236     *sStart = vStart;
237     *sEnd   = vEnd;
238     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
239     else *ft = PETSC_VTK_POINT_FIELD;
240   } else if (globalvcdof[1]) {
241     *sStart = cStart;
242     *sEnd   = cEnd;
243     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
244     else *ft = PETSC_VTK_CELL_FIELD;
245   } else {
246     if (field >= 0) {
247       const char *fieldname;
248 
249       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
250       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
251     } else {
252       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
253     }
254   }
255   PetscFunctionReturn(PETSC_SUCCESS);
256 }
257 
258 /*@
259   DMPlexVecView1D - Plot many 1D solutions on the same line graph
260 
261   Collective
262 
263   Input Parameters:
264 + dm     - The `DMPLEX` object
265 . n      - The number of vectors
266 . u      - The array of local vectors
267 - viewer - The `PetscViewer`
268 
269   Level: advanced
270 
271 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `VecViewFromOptions()`, `VecView()`
272 @*/
273 PetscErrorCode DMPlexVecView1D(DM dm, PetscInt n, Vec u[], PetscViewer viewer)
274 {
275   PetscDS            ds;
276   PetscDraw          draw = NULL;
277   PetscDrawLG        lg;
278   Vec                coordinates;
279   const PetscScalar *coords, **sol;
280   PetscReal         *vals;
281   PetscInt          *Nc;
282   PetscInt           Nf, f, c, Nl, l, i, vStart, vEnd, v;
283   char             **names;
284 
285   PetscFunctionBegin;
286   PetscCall(DMGetDS(dm, &ds));
287   PetscCall(PetscDSGetNumFields(ds, &Nf));
288   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
289   PetscCall(PetscDSGetComponents(ds, &Nc));
290 
291   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
292   if (!draw) PetscFunctionReturn(PETSC_SUCCESS);
293   PetscCall(PetscDrawLGCreate(draw, n * Nl, &lg));
294 
295   PetscCall(PetscMalloc3(n, &sol, n * Nl, &names, n * Nl, &vals));
296   for (i = 0, l = 0; i < n; ++i) {
297     const char *vname;
298 
299     PetscCall(PetscObjectGetName((PetscObject)u[i], &vname));
300     for (f = 0; f < Nf; ++f) {
301       PetscObject disc;
302       const char *fname;
303       char        tmpname[PETSC_MAX_PATH_LEN];
304 
305       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
306       /* TODO Create names for components */
307       for (c = 0; c < Nc[f]; ++c, ++l) {
308         PetscCall(PetscObjectGetName(disc, &fname));
309         PetscCall(PetscStrncpy(tmpname, vname, sizeof(tmpname)));
310         PetscCall(PetscStrlcat(tmpname, ":", sizeof(tmpname)));
311         PetscCall(PetscStrlcat(tmpname, fname, sizeof(tmpname)));
312         PetscCall(PetscStrallocpy(tmpname, &names[l]));
313       }
314     }
315   }
316   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *)names));
317   /* Just add P_1 support for now */
318   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
319   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
320   PetscCall(VecGetArrayRead(coordinates, &coords));
321   for (i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
322   for (v = vStart; v < vEnd; ++v) {
323     PetscScalar *x, *svals;
324 
325     PetscCall(DMPlexPointLocalRead(dm, v, coords, &x));
326     for (i = 0; i < n; ++i) {
327       PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
328       for (l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
329     }
330     PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
331   }
332   PetscCall(VecRestoreArrayRead(coordinates, &coords));
333   for (i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
334   for (l = 0; l < n * Nl; ++l) PetscCall(PetscFree(names[l]));
335   PetscCall(PetscFree3(sol, names, vals));
336 
337   PetscCall(PetscDrawLGDraw(lg));
338   PetscCall(PetscDrawLGDestroy(&lg));
339   PetscFunctionReturn(PETSC_SUCCESS);
340 }
341 
342 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer)
343 {
344   DM dm;
345 
346   PetscFunctionBegin;
347   PetscCall(VecGetDM(u, &dm));
348   PetscCall(DMPlexVecView1D(dm, 1, &u, viewer));
349   PetscFunctionReturn(PETSC_SUCCESS);
350 }
351 
352 static PetscErrorCode VecView_Plex_Local_Draw_2D(Vec v, PetscViewer viewer)
353 {
354   DM                 dm;
355   PetscSection       s;
356   PetscDraw          draw, popup;
357   DM                 cdm;
358   PetscSection       coordSection;
359   Vec                coordinates;
360   const PetscScalar *array;
361   PetscReal          lbound[3], ubound[3];
362   PetscReal          vbound[2], time;
363   PetscBool          flg;
364   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
365   const char        *name;
366   char               title[PETSC_MAX_PATH_LEN];
367 
368   PetscFunctionBegin;
369   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
370   PetscCall(VecGetDM(v, &dm));
371   PetscCall(DMGetCoordinateDim(dm, &dim));
372   PetscCall(DMGetLocalSection(dm, &s));
373   PetscCall(PetscSectionGetNumFields(s, &Nf));
374   PetscCall(DMGetCoarsenLevel(dm, &level));
375   PetscCall(DMGetCoordinateDM(dm, &cdm));
376   PetscCall(DMGetLocalSection(cdm, &coordSection));
377   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
378   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
379   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
380 
381   PetscCall(PetscObjectGetName((PetscObject)v, &name));
382   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
383 
384   PetscCall(VecGetLocalSize(coordinates, &N));
385   PetscCall(DMGetBoundingBox(dm, lbound, ubound));
386   PetscCall(PetscDrawClear(draw));
387 
388   /* Could implement something like DMDASelectFields() */
389   for (f = 0; f < Nf; ++f) {
390     DM          fdm = dm;
391     Vec         fv  = v;
392     IS          fis;
393     char        prefix[PETSC_MAX_PATH_LEN];
394     const char *fname;
395 
396     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
397     PetscCall(PetscSectionGetFieldName(s, f, &fname));
398 
399     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix, sizeof(prefix)));
400     else prefix[0] = '\0';
401     if (Nf > 1) {
402       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
403       PetscCall(VecGetSubVector(v, fis, &fv));
404       PetscCall(PetscStrlcat(prefix, fname, sizeof(prefix)));
405       PetscCall(PetscStrlcat(prefix, "_", sizeof(prefix)));
406     }
407     for (comp = 0; comp < Nc; ++comp, ++w) {
408       PetscInt nmax = 2;
409 
410       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
411       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
412       else PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
413       PetscCall(PetscDrawSetTitle(draw, title));
414 
415       /* TODO Get max and min only for this component */
416       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
417       if (!flg) {
418         PetscCall(VecMin(fv, NULL, &vbound[0]));
419         PetscCall(VecMax(fv, NULL, &vbound[1]));
420         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
421       }
422 
423       PetscCall(PetscDrawGetPopup(draw, &popup));
424       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
425       PetscCall(PetscDrawSetCoordinates(draw, lbound[0], lbound[1], ubound[0], ubound[1]));
426       PetscCall(VecGetArrayRead(fv, &array));
427       for (c = cStart; c < cEnd; ++c) {
428         PetscScalar       *coords = NULL, *a = NULL;
429         const PetscScalar *coords_arr;
430         PetscBool          isDG;
431         PetscInt           numCoords, color[4] = {-1, -1, -1, -1};
432 
433         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
434         if (a) {
435           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
436           color[1] = color[2] = color[3] = color[0];
437         } else {
438           PetscScalar *vals = NULL;
439           PetscInt     numVals, va;
440 
441           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
442           PetscCheck(numVals % Nc == 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "The number of components %" PetscInt_FMT " does not divide the number of values in the closure %" PetscInt_FMT, Nc, numVals);
443           switch (numVals / Nc) {
444           case 3: /* P1 Triangle */
445           case 4: /* P1 Quadrangle */
446             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
447             break;
448           case 6: /* P2 Triangle */
449           case 8: /* P2 Quadrangle */
450             for (va = 0; va < numVals / (Nc * 2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp + numVals / (Nc * 2)]), vbound[0], vbound[1]);
451             break;
452           default:
453             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals / Nc);
454           }
455           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
456         }
457         PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
458         switch (numCoords) {
459         case 6:
460         case 12: /* Localized triangle */
461           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
462           break;
463         case 8:
464         case 16: /* Localized quadrilateral */
465           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
466           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), color[2], color[3], color[0]));
467           break;
468         default:
469           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
470         }
471         PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
472       }
473       PetscCall(VecRestoreArrayRead(fv, &array));
474       PetscCall(PetscDrawFlush(draw));
475       PetscCall(PetscDrawPause(draw));
476       PetscCall(PetscDrawSave(draw));
477     }
478     if (Nf > 1) {
479       PetscCall(VecRestoreSubVector(v, fis, &fv));
480       PetscCall(ISDestroy(&fis));
481       PetscCall(DMDestroy(&fdm));
482     }
483   }
484   PetscFunctionReturn(PETSC_SUCCESS);
485 }
486 
487 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
488 {
489   DM        dm;
490   PetscDraw draw;
491   PetscInt  dim;
492   PetscBool isnull;
493 
494   PetscFunctionBegin;
495   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
496   PetscCall(PetscDrawIsNull(draw, &isnull));
497   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
498 
499   PetscCall(VecGetDM(v, &dm));
500   PetscCall(DMGetCoordinateDim(dm, &dim));
501   switch (dim) {
502   case 1:
503     PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));
504     break;
505   case 2:
506     PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));
507     break;
508   default:
509     SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
510   }
511   PetscFunctionReturn(PETSC_SUCCESS);
512 }
513 
514 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
515 {
516   DM                      dm;
517   Vec                     locv;
518   const char             *name;
519   PetscSection            section;
520   PetscInt                pStart, pEnd;
521   PetscInt                numFields;
522   PetscViewerVTKFieldType ft;
523 
524   PetscFunctionBegin;
525   PetscCall(VecGetDM(v, &dm));
526   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
527   PetscCall(PetscObjectGetName((PetscObject)v, &name));
528   PetscCall(PetscObjectSetName((PetscObject)locv, name));
529   PetscCall(VecCopy(v, locv));
530   PetscCall(DMGetLocalSection(dm, &section));
531   PetscCall(PetscSectionGetNumFields(section, &numFields));
532   if (!numFields) {
533     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
534     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE, (PetscObject)locv));
535   } else {
536     PetscInt f;
537 
538     for (f = 0; f < numFields; f++) {
539       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
540       if (ft == PETSC_VTK_INVALID) continue;
541       PetscCall(PetscObjectReference((PetscObject)locv));
542       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE, (PetscObject)locv));
543     }
544     PetscCall(VecDestroy(&locv));
545   }
546   PetscFunctionReturn(PETSC_SUCCESS);
547 }
548 
549 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
550 {
551   DM        dm;
552   PetscBool isvtk, ishdf5, isdraw, isglvis, iscgns;
553 
554   PetscFunctionBegin;
555   PetscCall(VecGetDM(v, &dm));
556   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
557   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
558   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
559   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
560   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
561   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
562   if (isvtk || ishdf5 || isdraw || isglvis || iscgns) {
563     PetscInt    i, numFields;
564     PetscObject fe;
565     PetscBool   fem  = PETSC_FALSE;
566     Vec         locv = v;
567     const char *name;
568     PetscInt    step;
569     PetscReal   time;
570 
571     PetscCall(DMGetNumFields(dm, &numFields));
572     for (i = 0; i < numFields; i++) {
573       PetscCall(DMGetField(dm, i, NULL, &fe));
574       if (fe->classid == PETSCFE_CLASSID) {
575         fem = PETSC_TRUE;
576         break;
577       }
578     }
579     if (fem) {
580       PetscObject isZero;
581 
582       PetscCall(DMGetLocalVector(dm, &locv));
583       PetscCall(PetscObjectGetName((PetscObject)v, &name));
584       PetscCall(PetscObjectSetName((PetscObject)locv, name));
585       PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
586       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
587       PetscCall(VecCopy(v, locv));
588       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
589       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
590     }
591     if (isvtk) {
592       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
593     } else if (ishdf5) {
594 #if defined(PETSC_HAVE_HDF5)
595       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
596 #else
597       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
598 #endif
599     } else if (isdraw) {
600       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
601     } else if (isglvis) {
602       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
603       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
604       PetscCall(VecView_GLVis(locv, viewer));
605     } else if (iscgns) {
606 #if defined(PETSC_HAVE_CGNS)
607       PetscCall(VecView_Plex_Local_CGNS(locv, viewer));
608 #else
609       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
610 #endif
611     }
612     if (fem) {
613       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
614       PetscCall(DMRestoreLocalVector(dm, &locv));
615     }
616   } else {
617     PetscBool isseq;
618 
619     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
620     if (isseq) PetscCall(VecView_Seq(v, viewer));
621     else PetscCall(VecView_MPI(v, viewer));
622   }
623   PetscFunctionReturn(PETSC_SUCCESS);
624 }
625 
626 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
627 {
628   DM        dm;
629   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii, iscgns;
630 
631   PetscFunctionBegin;
632   PetscCall(VecGetDM(v, &dm));
633   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
634   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
635   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
636   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
637   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
638   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
639   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
640   if (isvtk || isdraw || isglvis || iscgns) {
641     Vec         locv;
642     PetscObject isZero;
643     const char *name;
644 
645     PetscCall(DMGetLocalVector(dm, &locv));
646     PetscCall(PetscObjectGetName((PetscObject)v, &name));
647     PetscCall(PetscObjectSetName((PetscObject)locv, name));
648     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
649     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
650     PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
651     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
652     PetscCall(VecView_Plex_Local(locv, viewer));
653     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
654     PetscCall(DMRestoreLocalVector(dm, &locv));
655   } else if (ishdf5) {
656 #if defined(PETSC_HAVE_HDF5)
657     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
658 #else
659     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
660 #endif
661   } else if (isexodusii) {
662 #if defined(PETSC_HAVE_EXODUSII)
663     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
664 #else
665     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
666 #endif
667   } else {
668     PetscBool isseq;
669 
670     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
671     if (isseq) PetscCall(VecView_Seq(v, viewer));
672     else PetscCall(VecView_MPI(v, viewer));
673   }
674   PetscFunctionReturn(PETSC_SUCCESS);
675 }
676 
677 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
678 {
679   DM                dm;
680   MPI_Comm          comm;
681   PetscViewerFormat format;
682   Vec               v;
683   PetscBool         isvtk, ishdf5;
684 
685   PetscFunctionBegin;
686   PetscCall(VecGetDM(originalv, &dm));
687   PetscCall(PetscObjectGetComm((PetscObject)originalv, &comm));
688   PetscCheck(dm, comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
689   PetscCall(PetscViewerGetFormat(viewer, &format));
690   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
691   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
692   if (format == PETSC_VIEWER_NATIVE) {
693     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
694     /* this need a better fix */
695     if (dm->useNatural) {
696       if (dm->sfNatural) {
697         const char *vecname;
698         PetscInt    n, nroots;
699 
700         PetscCall(VecGetLocalSize(originalv, &n));
701         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
702         if (n == nroots) {
703           PetscCall(DMPlexCreateNaturalVector(dm, &v));
704           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
705           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
706           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
707           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
708         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
709       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
710     } else v = originalv;
711   } else v = originalv;
712 
713   if (ishdf5) {
714 #if defined(PETSC_HAVE_HDF5)
715     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
716 #else
717     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
718 #endif
719   } else if (isvtk) {
720     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
721   } else {
722     PetscBool isseq;
723 
724     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
725     if (isseq) PetscCall(VecView_Seq(v, viewer));
726     else PetscCall(VecView_MPI(v, viewer));
727   }
728   if (v != originalv) PetscCall(VecDestroy(&v));
729   PetscFunctionReturn(PETSC_SUCCESS);
730 }
731 
732 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
733 {
734   DM        dm;
735   PetscBool ishdf5;
736 
737   PetscFunctionBegin;
738   PetscCall(VecGetDM(v, &dm));
739   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
740   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
741   if (ishdf5) {
742     DM          dmBC;
743     Vec         gv;
744     const char *name;
745 
746     PetscCall(DMGetOutputDM(dm, &dmBC));
747     PetscCall(DMGetGlobalVector(dmBC, &gv));
748     PetscCall(PetscObjectGetName((PetscObject)v, &name));
749     PetscCall(PetscObjectSetName((PetscObject)gv, name));
750     PetscCall(VecLoad_Default(gv, viewer));
751     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
752     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
753     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
754   } else PetscCall(VecLoad_Default(v, viewer));
755   PetscFunctionReturn(PETSC_SUCCESS);
756 }
757 
758 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
759 {
760   DM        dm;
761   PetscBool ishdf5, isexodusii;
762 
763   PetscFunctionBegin;
764   PetscCall(VecGetDM(v, &dm));
765   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
766   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
767   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
768   if (ishdf5) {
769 #if defined(PETSC_HAVE_HDF5)
770     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
771 #else
772     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
773 #endif
774   } else if (isexodusii) {
775 #if defined(PETSC_HAVE_EXODUSII)
776     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
777 #else
778     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
779 #endif
780   } else PetscCall(VecLoad_Default(v, viewer));
781   PetscFunctionReturn(PETSC_SUCCESS);
782 }
783 
784 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
785 {
786   DM                dm;
787   PetscViewerFormat format;
788   PetscBool         ishdf5;
789 
790   PetscFunctionBegin;
791   PetscCall(VecGetDM(originalv, &dm));
792   PetscCheck(dm, PetscObjectComm((PetscObject)originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
793   PetscCall(PetscViewerGetFormat(viewer, &format));
794   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
795   if (format == PETSC_VIEWER_NATIVE) {
796     if (dm->useNatural) {
797       if (dm->sfNatural) {
798         if (ishdf5) {
799 #if defined(PETSC_HAVE_HDF5)
800           Vec         v;
801           const char *vecname;
802 
803           PetscCall(DMPlexCreateNaturalVector(dm, &v));
804           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
805           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
806           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
807           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
808           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
809           PetscCall(VecDestroy(&v));
810 #else
811           SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
812 #endif
813         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
814       }
815     } else PetscCall(VecLoad_Default(originalv, viewer));
816   }
817   PetscFunctionReturn(PETSC_SUCCESS);
818 }
819 
820 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
821 {
822   PetscSection       coordSection;
823   Vec                coordinates;
824   DMLabel            depthLabel, celltypeLabel;
825   const char        *name[4];
826   const PetscScalar *a;
827   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
828 
829   PetscFunctionBegin;
830   PetscCall(DMGetDimension(dm, &dim));
831   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
832   PetscCall(DMGetCoordinateSection(dm, &coordSection));
833   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
834   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
835   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
836   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
837   PetscCall(VecGetArrayRead(coordinates, &a));
838   name[0]       = "vertex";
839   name[1]       = "edge";
840   name[dim - 1] = "face";
841   name[dim]     = "cell";
842   for (c = cStart; c < cEnd; ++c) {
843     PetscInt *closure = NULL;
844     PetscInt  closureSize, cl, ct;
845 
846     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
847     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
848     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
849     PetscCall(PetscViewerASCIIPushTab(viewer));
850     for (cl = 0; cl < closureSize * 2; cl += 2) {
851       PetscInt point = closure[cl], depth, dof, off, d, p;
852 
853       if ((point < pStart) || (point >= pEnd)) continue;
854       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
855       if (!dof) continue;
856       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
857       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
858       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
859       for (p = 0; p < dof / dim; ++p) {
860         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
861         for (d = 0; d < dim; ++d) {
862           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
863           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double)PetscRealPart(a[off + p * dim + d])));
864         }
865         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
866       }
867       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
868     }
869     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
870     PetscCall(PetscViewerASCIIPopTab(viewer));
871   }
872   PetscCall(VecRestoreArrayRead(coordinates, &a));
873   PetscFunctionReturn(PETSC_SUCCESS);
874 }
875 
876 typedef enum {
877   CS_CARTESIAN,
878   CS_POLAR,
879   CS_CYLINDRICAL,
880   CS_SPHERICAL
881 } CoordSystem;
882 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
883 
884 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
885 {
886   PetscInt i;
887 
888   PetscFunctionBegin;
889   if (dim > 3) {
890     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)PetscRealPart(x[i])));
891   } else {
892     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
893 
894     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
895     switch (cs) {
896     case CS_CARTESIAN:
897       for (i = 0; i < dim; ++i) trcoords[i] = coords[i];
898       break;
899     case CS_POLAR:
900       PetscCheck(dim == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
901       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
902       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
903       break;
904     case CS_CYLINDRICAL:
905       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
906       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
907       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
908       trcoords[2] = coords[2];
909       break;
910     case CS_SPHERICAL:
911       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
912       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
913       trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
914       trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
915       break;
916     }
917     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)trcoords[i]));
918   }
919   PetscFunctionReturn(PETSC_SUCCESS);
920 }
921 
922 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
923 {
924   DM_Plex          *mesh = (DM_Plex *)dm->data;
925   DM                cdm, cdmCell;
926   PetscSection      coordSection, coordSectionCell;
927   Vec               coordinates, coordinatesCell;
928   PetscViewerFormat format;
929 
930   PetscFunctionBegin;
931   PetscCall(PetscViewerGetFormat(viewer, &format));
932   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
933     const char *name;
934     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
935     PetscInt    pStart, pEnd, p, numLabels, l;
936     PetscMPIInt rank, size;
937 
938     PetscCall(DMGetCoordinateDM(dm, &cdm));
939     PetscCall(DMGetCoordinateSection(dm, &coordSection));
940     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
941     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
942     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
943     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
944     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
945     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
946     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
947     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
948     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
949     PetscCall(DMGetDimension(dm, &dim));
950     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
951     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
952     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
953     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
954     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
955     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
956     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
957     for (p = pStart; p < pEnd; ++p) {
958       PetscInt dof, off, s;
959 
960       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
961       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
962       for (s = off; s < off + dof; ++s) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
963     }
964     PetscCall(PetscViewerFlush(viewer));
965     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
966     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
967     for (p = pStart; p < pEnd; ++p) {
968       PetscInt dof, off, c;
969 
970       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
971       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
972       for (c = off; c < off + dof; ++c) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " <---- %" PetscInt_FMT " (%" PetscInt_FMT ")\n", rank, p, mesh->cones[c], mesh->coneOrientations[c]));
973     }
974     PetscCall(PetscViewerFlush(viewer));
975     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
976     if (coordSection && coordinates) {
977       CoordSystem        cs = CS_CARTESIAN;
978       const PetscScalar *array, *arrayCell = NULL;
979       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_MAX_INT, pcEnd = PETSC_MIN_INT, pStart, pEnd, p;
980       PetscMPIInt        rank;
981       const char        *name;
982 
983       PetscCall(PetscOptionsGetEnum(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *)&cs, NULL));
984       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
985       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
986       PetscCheck(Nf == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
987       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
988       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
989       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
990       pStart = PetscMin(pvStart, pcStart);
991       pEnd   = PetscMax(pvEnd, pcEnd);
992       PetscCall(PetscObjectGetName((PetscObject)coordinates, &name));
993       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
994       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
995       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
996 
997       PetscCall(VecGetArrayRead(coordinates, &array));
998       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
999       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1000       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
1001       for (p = pStart; p < pEnd; ++p) {
1002         PetscInt dof, off;
1003 
1004         if (p >= pvStart && p < pvEnd) {
1005           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
1006           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
1007           if (dof) {
1008             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1009             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
1010             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1011           }
1012         }
1013         if (cdmCell && p >= pcStart && p < pcEnd) {
1014           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
1015           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
1016           if (dof) {
1017             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1018             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
1019             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1020           }
1021         }
1022       }
1023       PetscCall(PetscViewerFlush(viewer));
1024       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1025       PetscCall(VecRestoreArrayRead(coordinates, &array));
1026       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
1027     }
1028     PetscCall(DMGetNumLabels(dm, &numLabels));
1029     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1030     for (l = 0; l < numLabels; ++l) {
1031       DMLabel     label;
1032       PetscBool   isdepth;
1033       const char *name;
1034 
1035       PetscCall(DMGetLabelName(dm, l, &name));
1036       PetscCall(PetscStrcmp(name, "depth", &isdepth));
1037       if (isdepth) continue;
1038       PetscCall(DMGetLabel(dm, name, &label));
1039       PetscCall(DMLabelView(label, viewer));
1040     }
1041     if (size > 1) {
1042       PetscSF sf;
1043 
1044       PetscCall(DMGetPointSF(dm, &sf));
1045       PetscCall(PetscSFView(sf, viewer));
1046     }
1047     if (mesh->periodic.face_sf) PetscCall(PetscSFView(mesh->periodic.face_sf, viewer));
1048     PetscCall(PetscViewerFlush(viewer));
1049   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
1050     const char  *name, *color;
1051     const char  *defcolors[3]  = {"gray", "orange", "green"};
1052     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
1053     char         lname[PETSC_MAX_PATH_LEN];
1054     PetscReal    scale      = 2.0;
1055     PetscReal    tikzscale  = 1.0;
1056     PetscBool    useNumbers = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
1057     double       tcoords[3];
1058     PetscScalar *coords;
1059     PetscInt     numLabels, l, numColors, numLColors, dim, d, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, e, p, n;
1060     PetscMPIInt  rank, size;
1061     char       **names, **colors, **lcolors;
1062     PetscBool    flg, lflg;
1063     PetscBT      wp = NULL;
1064     PetscInt     pEnd, pStart;
1065 
1066     PetscCall(DMGetCoordinateDM(dm, &cdm));
1067     PetscCall(DMGetCoordinateSection(dm, &coordSection));
1068     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1069     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
1070     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
1071     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
1072     PetscCall(DMGetDimension(dm, &dim));
1073     PetscCall(DMPlexGetDepth(dm, &depth));
1074     PetscCall(DMGetNumLabels(dm, &numLabels));
1075     numLabels  = PetscMax(numLabels, 10);
1076     numColors  = 10;
1077     numLColors = 10;
1078     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
1079     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
1080     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
1081     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
1082     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
1083     for (d = 0; d < 4; ++d) drawColors[d] = PETSC_TRUE;
1084     n = 4;
1085     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
1086     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1087     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
1088     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1089     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
1090     if (!useLabels) numLabels = 0;
1091     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
1092     if (!useColors) {
1093       numColors = 3;
1094       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
1095     }
1096     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
1097     if (!useColors) {
1098       numLColors = 4;
1099       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
1100     }
1101     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
1102     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
1103     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
1104     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
1105     if (depth < dim) plotEdges = PETSC_FALSE;
1106     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
1107 
1108     /* filter points with labelvalue != labeldefaultvalue */
1109     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1110     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1111     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
1112     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1113     if (lflg) {
1114       DMLabel lbl;
1115 
1116       PetscCall(DMGetLabel(dm, lname, &lbl));
1117       if (lbl) {
1118         PetscInt val, defval;
1119 
1120         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
1121         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
1122         for (c = pStart; c < pEnd; c++) {
1123           PetscInt *closure = NULL;
1124           PetscInt  closureSize;
1125 
1126           PetscCall(DMLabelGetValue(lbl, c, &val));
1127           if (val == defval) continue;
1128 
1129           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1130           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
1131           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1132         }
1133       }
1134     }
1135 
1136     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1137     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1138     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1139     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1140 \\documentclass[tikz]{standalone}\n\n\
1141 \\usepackage{pgflibraryshapes}\n\
1142 \\usetikzlibrary{backgrounds}\n\
1143 \\usetikzlibrary{arrows}\n\
1144 \\begin{document}\n"));
1145     if (size > 1) {
1146       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1147       for (p = 0; p < size; ++p) {
1148         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1149         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1150       }
1151       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1152     }
1153     if (drawHasse) {
1154       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, cEnd - cStart));
1155 
1156       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1157       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1158       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1159       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1160       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1161       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1162       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1163       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1164       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1165       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1166       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1167       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1168     }
1169     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1170 
1171     /* Plot vertices */
1172     PetscCall(VecGetArray(coordinates, &coords));
1173     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1174     for (v = vStart; v < vEnd; ++v) {
1175       PetscInt  off, dof, d;
1176       PetscBool isLabeled = PETSC_FALSE;
1177 
1178       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1179       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1180       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1181       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1182       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1183       for (d = 0; d < dof; ++d) {
1184         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1185         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1186       }
1187       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1188       if (dim == 3) {
1189         PetscReal tmp = tcoords[1];
1190         tcoords[1]    = tcoords[2];
1191         tcoords[2]    = -tmp;
1192       }
1193       for (d = 0; d < dof; ++d) {
1194         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1195         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1196       }
1197       if (drawHasse) color = colors[0 % numColors];
1198       else color = colors[rank % numColors];
1199       for (l = 0; l < numLabels; ++l) {
1200         PetscInt val;
1201         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1202         if (val >= 0) {
1203           color     = lcolors[l % numLColors];
1204           isLabeled = PETSC_TRUE;
1205           break;
1206         }
1207       }
1208       if (drawNumbers[0]) {
1209         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1210       } else if (drawColors[0]) {
1211         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1212       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1213     }
1214     PetscCall(VecRestoreArray(coordinates, &coords));
1215     PetscCall(PetscViewerFlush(viewer));
1216     /* Plot edges */
1217     if (plotEdges) {
1218       PetscCall(VecGetArray(coordinates, &coords));
1219       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1220       for (e = eStart; e < eEnd; ++e) {
1221         const PetscInt *cone;
1222         PetscInt        coneSize, offA, offB, dof, d;
1223 
1224         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1225         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1226         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1227         PetscCall(DMPlexGetCone(dm, e, &cone));
1228         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1229         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1230         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1231         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1232         for (d = 0; d < dof; ++d) {
1233           tcoords[d] = (double)(0.5 * scale * PetscRealPart(coords[offA + d] + coords[offB + d]));
1234           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1235         }
1236         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1237         if (dim == 3) {
1238           PetscReal tmp = tcoords[1];
1239           tcoords[1]    = tcoords[2];
1240           tcoords[2]    = -tmp;
1241         }
1242         for (d = 0; d < dof; ++d) {
1243           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1244           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1245         }
1246         if (drawHasse) color = colors[1 % numColors];
1247         else color = colors[rank % numColors];
1248         for (l = 0; l < numLabels; ++l) {
1249           PetscInt val;
1250           PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1251           if (val >= 0) {
1252             color = lcolors[l % numLColors];
1253             break;
1254           }
1255         }
1256         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1257       }
1258       PetscCall(VecRestoreArray(coordinates, &coords));
1259       PetscCall(PetscViewerFlush(viewer));
1260       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1261     }
1262     /* Plot cells */
1263     if (dim == 3 || !drawNumbers[1]) {
1264       for (e = eStart; e < eEnd; ++e) {
1265         const PetscInt *cone;
1266 
1267         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1268         color = colors[rank % numColors];
1269         for (l = 0; l < numLabels; ++l) {
1270           PetscInt val;
1271           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1272           if (val >= 0) {
1273             color = lcolors[l % numLColors];
1274             break;
1275           }
1276         }
1277         PetscCall(DMPlexGetCone(dm, e, &cone));
1278         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1279       }
1280     } else {
1281       DMPolytopeType ct;
1282 
1283       /* Drawing a 2D polygon */
1284       for (c = cStart; c < cEnd; ++c) {
1285         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1286         PetscCall(DMPlexGetCellType(dm, c, &ct));
1287         if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
1288           const PetscInt *cone;
1289           PetscInt        coneSize, e;
1290 
1291           PetscCall(DMPlexGetCone(dm, c, &cone));
1292           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1293           for (e = 0; e < coneSize; ++e) {
1294             const PetscInt *econe;
1295 
1296             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1297             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", colors[rank % numColors], econe[0], rank, cone[e], rank, econe[1], rank));
1298           }
1299         } else {
1300           PetscInt *closure = NULL;
1301           PetscInt  closureSize, Nv = 0, v;
1302 
1303           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1304           for (p = 0; p < closureSize * 2; p += 2) {
1305             const PetscInt point = closure[p];
1306 
1307             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1308           }
1309           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1310           for (v = 0; v <= Nv; ++v) {
1311             const PetscInt vertex = closure[v % Nv];
1312 
1313             if (v > 0) {
1314               if (plotEdges) {
1315                 const PetscInt *edge;
1316                 PetscInt        endpoints[2], ne;
1317 
1318                 endpoints[0] = closure[v - 1];
1319                 endpoints[1] = vertex;
1320                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1321                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1322                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1323                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1324               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1325             }
1326             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1327           }
1328           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1329           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1330         }
1331       }
1332     }
1333     for (c = cStart; c < cEnd; ++c) {
1334       double             ccoords[3] = {0.0, 0.0, 0.0};
1335       PetscBool          isLabeled  = PETSC_FALSE;
1336       PetscScalar       *cellCoords = NULL;
1337       const PetscScalar *array;
1338       PetscInt           numCoords, cdim, d;
1339       PetscBool          isDG;
1340 
1341       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1342       PetscCall(DMGetCoordinateDim(dm, &cdim));
1343       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1344       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1345       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1346       for (p = 0; p < numCoords / cdim; ++p) {
1347         for (d = 0; d < cdim; ++d) {
1348           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1349           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1350         }
1351         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1352         if (cdim == 3) {
1353           PetscReal tmp = tcoords[1];
1354           tcoords[1]    = tcoords[2];
1355           tcoords[2]    = -tmp;
1356         }
1357         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1358       }
1359       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1360       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1361       for (d = 0; d < cdim; ++d) {
1362         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1363         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)ccoords[d]));
1364       }
1365       if (drawHasse) color = colors[depth % numColors];
1366       else color = colors[rank % numColors];
1367       for (l = 0; l < numLabels; ++l) {
1368         PetscInt val;
1369         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1370         if (val >= 0) {
1371           color     = lcolors[l % numLColors];
1372           isLabeled = PETSC_TRUE;
1373           break;
1374         }
1375       }
1376       if (drawNumbers[dim]) {
1377         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1378       } else if (drawColors[dim]) {
1379         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1380       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1381     }
1382     if (drawHasse) {
1383       color = colors[depth % numColors];
1384       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1385       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1386       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1387       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,0) {\\c};\n", rank, color));
1388       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1389 
1390       color = colors[1 % numColors];
1391       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1392       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1393       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1394       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,1) {\\e};\n", rank, color));
1395       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1396 
1397       color = colors[0 % numColors];
1398       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1399       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1400       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1401       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,2) {\\v};\n", rank, color));
1402       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1403 
1404       for (p = pStart; p < pEnd; ++p) {
1405         const PetscInt *cone;
1406         PetscInt        coneSize, cp;
1407 
1408         PetscCall(DMPlexGetCone(dm, p, &cone));
1409         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1410         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1411       }
1412     }
1413     PetscCall(PetscViewerFlush(viewer));
1414     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1415     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1416     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1417     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1418     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1419     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1420     PetscCall(PetscFree3(names, colors, lcolors));
1421     PetscCall(PetscBTDestroy(&wp));
1422   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1423     Vec                    cown, acown;
1424     VecScatter             sct;
1425     ISLocalToGlobalMapping g2l;
1426     IS                     gid, acis;
1427     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1428     MPI_Group              ggroup, ngroup;
1429     PetscScalar           *array, nid;
1430     const PetscInt        *idxs;
1431     PetscInt              *idxs2, *start, *adjacency, *work;
1432     PetscInt64             lm[3], gm[3];
1433     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1434     PetscMPIInt            d1, d2, rank;
1435 
1436     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1437     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1438 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1439     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1440 #endif
1441     if (ncomm != MPI_COMM_NULL) {
1442       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1443       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1444       d1 = 0;
1445       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1446       nid = d2;
1447       PetscCallMPI(MPI_Group_free(&ggroup));
1448       PetscCallMPI(MPI_Group_free(&ngroup));
1449       PetscCallMPI(MPI_Comm_free(&ncomm));
1450     } else nid = 0.0;
1451 
1452     /* Get connectivity */
1453     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1454     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1455 
1456     /* filter overlapped local cells */
1457     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1458     PetscCall(ISGetIndices(gid, &idxs));
1459     PetscCall(ISGetLocalSize(gid, &cum));
1460     PetscCall(PetscMalloc1(cum, &idxs2));
1461     for (c = cStart, cum = 0; c < cEnd; c++) {
1462       if (idxs[c - cStart] < 0) continue;
1463       idxs2[cum++] = idxs[c - cStart];
1464     }
1465     PetscCall(ISRestoreIndices(gid, &idxs));
1466     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1467     PetscCall(ISDestroy(&gid));
1468     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1469 
1470     /* support for node-aware cell locality */
1471     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1472     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1473     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1474     PetscCall(VecGetArray(cown, &array));
1475     for (c = 0; c < numVertices; c++) array[c] = nid;
1476     PetscCall(VecRestoreArray(cown, &array));
1477     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1478     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1479     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1480     PetscCall(ISDestroy(&acis));
1481     PetscCall(VecScatterDestroy(&sct));
1482     PetscCall(VecDestroy(&cown));
1483 
1484     /* compute edgeCut */
1485     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1486     PetscCall(PetscMalloc1(cum, &work));
1487     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1488     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1489     PetscCall(ISDestroy(&gid));
1490     PetscCall(VecGetArray(acown, &array));
1491     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1492       PetscInt totl;
1493 
1494       totl = start[c + 1] - start[c];
1495       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1496       for (i = 0; i < totl; i++) {
1497         if (work[i] < 0) {
1498           ect += 1;
1499           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1500         }
1501       }
1502     }
1503     PetscCall(PetscFree(work));
1504     PetscCall(VecRestoreArray(acown, &array));
1505     lm[0] = numVertices > 0 ? numVertices : PETSC_MAX_INT;
1506     lm[1] = -numVertices;
1507     PetscCall(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1508     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT, -((double)gm[1]) / ((double)gm[0]), -(PetscInt)gm[1], (PetscInt)gm[0]));
1509     lm[0] = ect;                     /* edgeCut */
1510     lm[1] = ectn;                    /* node-aware edgeCut */
1511     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1512     PetscCall(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1513     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt_FMT ")\n", (PetscInt)gm[2]));
1514 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1515     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), gm[0] ? ((double)(gm[1])) / ((double)gm[0]) : 1.));
1516 #else
1517     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), 0.0));
1518 #endif
1519     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1520     PetscCall(PetscFree(start));
1521     PetscCall(PetscFree(adjacency));
1522     PetscCall(VecDestroy(&acown));
1523   } else {
1524     const char    *name;
1525     PetscInt      *sizes, *hybsizes, *ghostsizes;
1526     PetscInt       locDepth, depth, cellHeight, dim, d;
1527     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1528     PetscInt       numLabels, l, maxSize = 17;
1529     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1530     MPI_Comm       comm;
1531     PetscMPIInt    size, rank;
1532 
1533     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1534     PetscCallMPI(MPI_Comm_size(comm, &size));
1535     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1536     PetscCall(DMGetDimension(dm, &dim));
1537     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1538     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1539     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1540     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1541     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1542     PetscCall(DMPlexGetDepth(dm, &locDepth));
1543     PetscCall(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1544     PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &gcStart, &gcEnd));
1545     gcNum = gcEnd - gcStart;
1546     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1547     else PetscCall(PetscCalloc3(3, &sizes, 3, &hybsizes, 3, &ghostsizes));
1548     for (d = 0; d <= depth; d++) {
1549       PetscInt Nc[2] = {0, 0}, ict;
1550 
1551       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1552       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1553       ict = ct0;
1554       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1555       ct0 = (DMPolytopeType)ict;
1556       for (p = pStart; p < pEnd; ++p) {
1557         DMPolytopeType ct;
1558 
1559         PetscCall(DMPlexGetCellType(dm, p, &ct));
1560         if (ct == ct0) ++Nc[0];
1561         else ++Nc[1];
1562       }
1563       if (size < maxSize) {
1564         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes, 1, MPIU_INT, 0, comm));
1565         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1566         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1567         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1568         for (p = 0; p < size; ++p) {
1569           if (rank == 0) {
1570             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p] + hybsizes[p]));
1571             if (hybsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1572             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1573           }
1574         }
1575       } else {
1576         PetscInt locMinMax[2];
1577 
1578         locMinMax[0] = Nc[0] + Nc[1];
1579         locMinMax[1] = Nc[0] + Nc[1];
1580         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1581         locMinMax[0] = Nc[1];
1582         locMinMax[1] = Nc[1];
1583         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1584         if (d == depth) {
1585           locMinMax[0] = gcNum;
1586           locMinMax[1] = gcNum;
1587           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1588         }
1589         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1590         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1591         if (hybsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1592         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1593       }
1594       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1595     }
1596     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1597     {
1598       const PetscReal *maxCell;
1599       const PetscReal *L;
1600       PetscBool        localized;
1601 
1602       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1603       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1604       if (L || localized) {
1605         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1606         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1607         if (L) {
1608           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1609           for (d = 0; d < dim; ++d) {
1610             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1611             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1612           }
1613           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1614         }
1615         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1616         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1617       }
1618     }
1619     PetscCall(DMGetNumLabels(dm, &numLabels));
1620     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1621     for (l = 0; l < numLabels; ++l) {
1622       DMLabel         label;
1623       const char     *name;
1624       IS              valueIS;
1625       const PetscInt *values;
1626       PetscInt        numValues, v;
1627 
1628       PetscCall(DMGetLabelName(dm, l, &name));
1629       PetscCall(DMGetLabel(dm, name, &label));
1630       PetscCall(DMLabelGetNumValues(label, &numValues));
1631       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1632       PetscCall(DMLabelGetValueIS(label, &valueIS));
1633       PetscCall(ISGetIndices(valueIS, &values));
1634       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1635       for (v = 0; v < numValues; ++v) {
1636         PetscInt size;
1637 
1638         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1639         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1640         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1641       }
1642       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1643       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1644       PetscCall(ISRestoreIndices(valueIS, &values));
1645       PetscCall(ISDestroy(&valueIS));
1646     }
1647     {
1648       char    **labelNames;
1649       PetscInt  Nl = numLabels;
1650       PetscBool flg;
1651 
1652       PetscCall(PetscMalloc1(Nl, &labelNames));
1653       PetscCall(PetscOptionsGetStringArray(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1654       for (l = 0; l < Nl; ++l) {
1655         DMLabel label;
1656 
1657         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1658         if (flg) {
1659           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1660           PetscCall(DMLabelView(label, viewer));
1661         }
1662         PetscCall(PetscFree(labelNames[l]));
1663       }
1664       PetscCall(PetscFree(labelNames));
1665     }
1666     /* If no fields are specified, people do not want to see adjacency */
1667     if (dm->Nf) {
1668       PetscInt f;
1669 
1670       for (f = 0; f < dm->Nf; ++f) {
1671         const char *name;
1672 
1673         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1674         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1675         PetscCall(PetscViewerASCIIPushTab(viewer));
1676         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1677         if (dm->fields[f].adjacency[0]) {
1678           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1679           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1680         } else {
1681           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1682           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1683         }
1684         PetscCall(PetscViewerASCIIPopTab(viewer));
1685       }
1686     }
1687     PetscCall(DMGetCoarseDM(dm, &cdm));
1688     if (cdm) {
1689       PetscCall(PetscViewerASCIIPushTab(viewer));
1690       PetscCall(PetscViewerASCIIPrintf(viewer, "Defined by transform from:\n"));
1691       PetscCall(DMPlexView_Ascii(cdm, viewer));
1692       PetscCall(PetscViewerASCIIPopTab(viewer));
1693     }
1694   }
1695   PetscFunctionReturn(PETSC_SUCCESS);
1696 }
1697 
1698 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1699 {
1700   DMPolytopeType ct;
1701   PetscMPIInt    rank;
1702   PetscInt       cdim;
1703 
1704   PetscFunctionBegin;
1705   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1706   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1707   PetscCall(DMGetCoordinateDim(dm, &cdim));
1708   switch (ct) {
1709   case DM_POLYTOPE_SEGMENT:
1710   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1711     switch (cdim) {
1712     case 1: {
1713       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1714       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1715 
1716       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, PETSC_DRAW_BLACK));
1717       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, PETSC_DRAW_BLACK));
1718       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, PETSC_DRAW_BLACK));
1719     } break;
1720     case 2: {
1721       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1722       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1723       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1724 
1725       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1726       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]) + l * dx, PetscRealPart(coords[1]) + l * dy, PetscRealPart(coords[0]) - l * dx, PetscRealPart(coords[1]) - l * dy, PETSC_DRAW_BLACK));
1727       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]) + l * dx, PetscRealPart(coords[3]) + l * dy, PetscRealPart(coords[2]) - l * dx, PetscRealPart(coords[3]) - l * dy, PETSC_DRAW_BLACK));
1728     } break;
1729     default:
1730       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1731     }
1732     break;
1733   case DM_POLYTOPE_TRIANGLE:
1734     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1735     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1736     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1737     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1738     break;
1739   case DM_POLYTOPE_QUADRILATERAL:
1740     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1741     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1742     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1743     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1744     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1745     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1746     break;
1747   case DM_POLYTOPE_SEG_PRISM_TENSOR:
1748     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1749     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1750     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1751     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1752     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1753     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1754     break;
1755   case DM_POLYTOPE_FV_GHOST:
1756     break;
1757   default:
1758     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1759   }
1760   PetscFunctionReturn(PETSC_SUCCESS);
1761 }
1762 
1763 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1764 {
1765   DMPolytopeType ct;
1766   PetscReal      centroid[2] = {0., 0.};
1767   PetscMPIInt    rank;
1768   PetscInt       fillColor, v, e, d;
1769 
1770   PetscFunctionBegin;
1771   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1772   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1773   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1774   switch (ct) {
1775   case DM_POLYTOPE_TRIANGLE: {
1776     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1777 
1778     for (v = 0; v < 3; ++v) {
1779       centroid[0] += PetscRealPart(coords[v * 2 + 0]) / 3.;
1780       centroid[1] += PetscRealPart(coords[v * 2 + 1]) / 3.;
1781     }
1782     for (e = 0; e < 3; ++e) {
1783       refCoords[0] = refVertices[e * 2 + 0];
1784       refCoords[1] = refVertices[e * 2 + 1];
1785       for (d = 1; d <= edgeDiv; ++d) {
1786         refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % 3 * 2 + 0] - refCoords[0]) * d / edgeDiv;
1787         refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % 3 * 2 + 1] - refCoords[1]) * d / edgeDiv;
1788       }
1789       PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1790       for (d = 0; d < edgeDiv; ++d) {
1791         PetscCall(PetscDrawTriangle(draw, centroid[0], centroid[1], edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], fillColor, fillColor, fillColor));
1792         PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1793       }
1794     }
1795   } break;
1796   default:
1797     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1798   }
1799   PetscFunctionReturn(PETSC_SUCCESS);
1800 }
1801 
1802 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1803 {
1804   PetscDraw    draw;
1805   DM           cdm;
1806   PetscSection coordSection;
1807   Vec          coordinates;
1808   PetscReal    xyl[3], xyr[3];
1809   PetscReal   *refCoords, *edgeCoords;
1810   PetscBool    isnull, drawAffine = PETSC_TRUE;
1811   PetscInt     dim, vStart, vEnd, cStart, cEnd, c, edgeDiv = 4;
1812 
1813   PetscFunctionBegin;
1814   PetscCall(DMGetCoordinateDim(dm, &dim));
1815   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1816   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1817   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1818   PetscCall(DMGetCoordinateDM(dm, &cdm));
1819   PetscCall(DMGetLocalSection(cdm, &coordSection));
1820   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1821   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1822   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1823 
1824   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1825   PetscCall(PetscDrawIsNull(draw, &isnull));
1826   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
1827   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1828 
1829   PetscCall(DMGetBoundingBox(dm, xyl, xyr));
1830   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1831   PetscCall(PetscDrawClear(draw));
1832 
1833   for (c = cStart; c < cEnd; ++c) {
1834     PetscScalar       *coords = NULL;
1835     const PetscScalar *coords_arr;
1836     PetscInt           numCoords;
1837     PetscBool          isDG;
1838 
1839     PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1840     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1841     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1842     PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1843   }
1844   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1845   PetscCall(PetscDrawFlush(draw));
1846   PetscCall(PetscDrawPause(draw));
1847   PetscCall(PetscDrawSave(draw));
1848   PetscFunctionReturn(PETSC_SUCCESS);
1849 }
1850 
1851 static PetscErrorCode DMPlexCreateHighOrderSurrogate_Internal(DM dm, DM *hdm)
1852 {
1853   DM           odm = dm, rdm = dm, cdm;
1854   PetscFE      fe;
1855   PetscSpace   sp;
1856   PetscClassId id;
1857   PetscInt     degree;
1858   PetscBool    hoView = PETSC_TRUE;
1859 
1860   PetscFunctionBegin;
1861   PetscObjectOptionsBegin((PetscObject)dm);
1862   PetscCall(PetscOptionsBool("-dm_plex_high_order_view", "Subsample to view meshes with high order coordinates", "DMPlexCreateHighOrderSurrogate_Internal", hoView, &hoView, NULL));
1863   PetscOptionsEnd();
1864   PetscCall(PetscObjectReference((PetscObject)dm));
1865   *hdm = dm;
1866   if (!hoView) PetscFunctionReturn(PETSC_SUCCESS);
1867   PetscCall(DMGetCoordinateDM(dm, &cdm));
1868   PetscCall(DMGetField(cdm, 0, NULL, (PetscObject *)&fe));
1869   PetscCall(PetscObjectGetClassId((PetscObject)fe, &id));
1870   if (id != PETSCFE_CLASSID) PetscFunctionReturn(PETSC_SUCCESS);
1871   PetscCall(PetscFEGetBasisSpace(fe, &sp));
1872   PetscCall(PetscSpaceGetDegree(sp, &degree, NULL));
1873   for (PetscInt r = 0, rd = PetscCeilReal(((PetscReal)degree) / 2.); r < (PetscInt)PetscCeilReal(PetscLog2Real(degree)); ++r, rd = PetscCeilReal(((PetscReal)rd) / 2.)) {
1874     DM  cdm, rcdm;
1875     Mat In;
1876     Vec cl, rcl;
1877 
1878     PetscCall(DMRefine(odm, PetscObjectComm((PetscObject)odm), &rdm));
1879     if (rd > 1) PetscCall(DMPlexCreateCoordinateSpace(rdm, rd, PETSC_FALSE, NULL));
1880     PetscCall(PetscObjectSetName((PetscObject)rdm, "Refined Mesh with Linear Coordinates"));
1881     PetscCall(DMGetCoordinateDM(odm, &cdm));
1882     PetscCall(DMGetCoordinateDM(rdm, &rcdm));
1883     PetscCall(DMGetCoordinatesLocal(odm, &cl));
1884     PetscCall(DMGetCoordinatesLocal(rdm, &rcl));
1885     PetscCall(DMSetCoarseDM(rcdm, cdm));
1886     PetscCall(DMCreateInterpolation(cdm, rcdm, &In, NULL));
1887     PetscCall(MatMult(In, cl, rcl));
1888     PetscCall(MatDestroy(&In));
1889     PetscCall(DMSetCoordinatesLocal(rdm, rcl));
1890     PetscCall(DMDestroy(&odm));
1891     odm = rdm;
1892   }
1893   *hdm = rdm;
1894   PetscFunctionReturn(PETSC_SUCCESS);
1895 }
1896 
1897 #if defined(PETSC_HAVE_EXODUSII)
1898   #include <exodusII.h>
1899   #include <petscviewerexodusii.h>
1900 #endif
1901 
1902 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1903 {
1904   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns;
1905   char      name[PETSC_MAX_PATH_LEN];
1906 
1907   PetscFunctionBegin;
1908   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1909   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1910   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
1911   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
1912   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1913   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
1914   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
1915   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
1916   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
1917   if (iascii) {
1918     PetscViewerFormat format;
1919     PetscCall(PetscViewerGetFormat(viewer, &format));
1920     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1921     else PetscCall(DMPlexView_Ascii(dm, viewer));
1922   } else if (ishdf5) {
1923 #if defined(PETSC_HAVE_HDF5)
1924     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1925 #else
1926     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1927 #endif
1928   } else if (isvtk) {
1929     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
1930   } else if (isdraw) {
1931     DM hdm;
1932 
1933     PetscCall(DMPlexCreateHighOrderSurrogate_Internal(dm, &hdm));
1934     PetscCall(DMPlexView_Draw(hdm, viewer));
1935     PetscCall(DMDestroy(&hdm));
1936   } else if (isglvis) {
1937     PetscCall(DMPlexView_GLVis(dm, viewer));
1938 #if defined(PETSC_HAVE_EXODUSII)
1939   } else if (isexodus) {
1940     /*
1941       exodusII requires that all sets be part of exactly one cell set.
1942       If the dm does not have a "Cell Sets" label defined, we create one
1943       with ID 1, containing all cells.
1944       Note that if the Cell Sets label is defined but does not cover all cells,
1945       we may still have a problem. This should probably be checked here or in the viewer;
1946     */
1947     PetscInt numCS;
1948     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
1949     if (!numCS) {
1950       PetscInt cStart, cEnd, c;
1951       PetscCall(DMCreateLabel(dm, "Cell Sets"));
1952       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1953       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
1954     }
1955     PetscCall(DMView_PlexExodusII(dm, viewer));
1956 #endif
1957 #if defined(PETSC_HAVE_CGNS)
1958   } else if (iscgns) {
1959     PetscCall(DMView_PlexCGNS(dm, viewer));
1960 #endif
1961   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
1962   /* Optionally view the partition */
1963   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
1964   if (flg) {
1965     Vec ranks;
1966     PetscCall(DMPlexCreateRankField(dm, &ranks));
1967     PetscCall(VecView(ranks, viewer));
1968     PetscCall(VecDestroy(&ranks));
1969   }
1970   /* Optionally view a label */
1971   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
1972   if (flg) {
1973     DMLabel label;
1974     Vec     val;
1975 
1976     PetscCall(DMGetLabel(dm, name, &label));
1977     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
1978     PetscCall(DMPlexCreateLabelField(dm, label, &val));
1979     PetscCall(VecView(val, viewer));
1980     PetscCall(VecDestroy(&val));
1981   }
1982   PetscFunctionReturn(PETSC_SUCCESS);
1983 }
1984 
1985 /*@
1986   DMPlexTopologyView - Saves a `DMPLEX` topology into a file
1987 
1988   Collective
1989 
1990   Input Parameters:
1991 + dm     - The `DM` whose topology is to be saved
1992 - viewer - The `PetscViewer` to save it in
1993 
1994   Level: advanced
1995 
1996 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`, `PetscViewer`
1997 @*/
1998 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
1999 {
2000   PetscBool ishdf5;
2001 
2002   PetscFunctionBegin;
2003   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2004   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2005   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2006   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
2007   if (ishdf5) {
2008 #if defined(PETSC_HAVE_HDF5)
2009     PetscViewerFormat format;
2010     PetscCall(PetscViewerGetFormat(viewer, &format));
2011     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2012       IS globalPointNumbering;
2013 
2014       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2015       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
2016       PetscCall(ISDestroy(&globalPointNumbering));
2017     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2018 #else
2019     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2020 #endif
2021   }
2022   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
2023   PetscFunctionReturn(PETSC_SUCCESS);
2024 }
2025 
2026 /*@
2027   DMPlexCoordinatesView - Saves `DMPLEX` coordinates into a file
2028 
2029   Collective
2030 
2031   Input Parameters:
2032 + dm     - The `DM` whose coordinates are to be saved
2033 - viewer - The `PetscViewer` for saving
2034 
2035   Level: advanced
2036 
2037 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`, `PetscViewer`
2038 @*/
2039 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
2040 {
2041   PetscBool ishdf5;
2042 
2043   PetscFunctionBegin;
2044   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2045   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2046   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2047   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2048   if (ishdf5) {
2049 #if defined(PETSC_HAVE_HDF5)
2050     PetscViewerFormat format;
2051     PetscCall(PetscViewerGetFormat(viewer, &format));
2052     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2053       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
2054     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2055 #else
2056     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2057 #endif
2058   }
2059   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2060   PetscFunctionReturn(PETSC_SUCCESS);
2061 }
2062 
2063 /*@
2064   DMPlexLabelsView - Saves `DMPLEX` labels into a file
2065 
2066   Collective
2067 
2068   Input Parameters:
2069 + dm     - The `DM` whose labels are to be saved
2070 - viewer - The `PetscViewer` for saving
2071 
2072   Level: advanced
2073 
2074 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`, `PetscViewer`
2075 @*/
2076 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
2077 {
2078   PetscBool ishdf5;
2079 
2080   PetscFunctionBegin;
2081   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2082   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2083   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2084   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
2085   if (ishdf5) {
2086 #if defined(PETSC_HAVE_HDF5)
2087     IS                globalPointNumbering;
2088     PetscViewerFormat format;
2089 
2090     PetscCall(PetscViewerGetFormat(viewer, &format));
2091     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2092       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2093       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
2094       PetscCall(ISDestroy(&globalPointNumbering));
2095     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2096 #else
2097     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2098 #endif
2099   }
2100   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
2101   PetscFunctionReturn(PETSC_SUCCESS);
2102 }
2103 
2104 /*@
2105   DMPlexSectionView - Saves a section associated with a `DMPLEX`
2106 
2107   Collective
2108 
2109   Input Parameters:
2110 + dm        - The `DM` that contains the topology on which the section to be saved is defined
2111 . viewer    - The `PetscViewer` for saving
2112 - sectiondm - The `DM` that contains the section to be saved
2113 
2114   Level: advanced
2115 
2116   Notes:
2117   This function is a wrapper around `PetscSectionView()`; in addition to the raw section, it saves information that associates the section points to the topology (dm) points. When the topology (dm) and the section are later loaded with `DMPlexTopologyLoad()` and `DMPlexSectionLoad()`, respectively, this information is used to match section points with topology points.
2118 
2119   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2120 
2121 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`, `PetscViewer`
2122 @*/
2123 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
2124 {
2125   PetscBool ishdf5;
2126 
2127   PetscFunctionBegin;
2128   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2129   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2130   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2131   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2132   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
2133   if (ishdf5) {
2134 #if defined(PETSC_HAVE_HDF5)
2135     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
2136 #else
2137     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2138 #endif
2139   }
2140   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
2141   PetscFunctionReturn(PETSC_SUCCESS);
2142 }
2143 
2144 /*@
2145   DMPlexGlobalVectorView - Saves a global vector
2146 
2147   Collective
2148 
2149   Input Parameters:
2150 + dm        - The `DM` that represents the topology
2151 . viewer    - The `PetscViewer` to save data with
2152 . sectiondm - The `DM` that contains the global section on which vec is defined
2153 - vec       - The global vector to be saved
2154 
2155   Level: advanced
2156 
2157   Notes:
2158   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2159 
2160   Calling sequence:
2161 .vb
2162        DMCreate(PETSC_COMM_WORLD, &dm);
2163        DMSetType(dm, DMPLEX);
2164        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2165        DMClone(dm, &sectiondm);
2166        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2167        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2168        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2169        PetscSectionSetChart(section, pStart, pEnd);
2170        PetscSectionSetUp(section);
2171        DMSetLocalSection(sectiondm, section);
2172        PetscSectionDestroy(&section);
2173        DMGetGlobalVector(sectiondm, &vec);
2174        PetscObjectSetName((PetscObject)vec, "vec_name");
2175        DMPlexTopologyView(dm, viewer);
2176        DMPlexSectionView(dm, viewer, sectiondm);
2177        DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
2178        DMRestoreGlobalVector(sectiondm, &vec);
2179        DMDestroy(&sectiondm);
2180        DMDestroy(&dm);
2181 .ve
2182 
2183 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2184 @*/
2185 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2186 {
2187   PetscBool ishdf5;
2188 
2189   PetscFunctionBegin;
2190   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2191   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2192   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2193   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2194   /* Check consistency */
2195   {
2196     PetscSection section;
2197     PetscBool    includesConstraints;
2198     PetscInt     m, m1;
2199 
2200     PetscCall(VecGetLocalSize(vec, &m1));
2201     PetscCall(DMGetGlobalSection(sectiondm, &section));
2202     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2203     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2204     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2205     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2206   }
2207   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2208   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2209   if (ishdf5) {
2210 #if defined(PETSC_HAVE_HDF5)
2211     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2212 #else
2213     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2214 #endif
2215   }
2216   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2217   PetscFunctionReturn(PETSC_SUCCESS);
2218 }
2219 
2220 /*@
2221   DMPlexLocalVectorView - Saves a local vector
2222 
2223   Collective
2224 
2225   Input Parameters:
2226 + dm        - The `DM` that represents the topology
2227 . viewer    - The `PetscViewer` to save data with
2228 . sectiondm - The `DM` that contains the local section on which `vec` is defined; may be the same as `dm`
2229 - vec       - The local vector to be saved
2230 
2231   Level: advanced
2232 
2233   Note:
2234   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2235 
2236   Calling sequence:
2237 .vb
2238        DMCreate(PETSC_COMM_WORLD, &dm);
2239        DMSetType(dm, DMPLEX);
2240        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2241        DMClone(dm, &sectiondm);
2242        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2243        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2244        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2245        PetscSectionSetChart(section, pStart, pEnd);
2246        PetscSectionSetUp(section);
2247        DMSetLocalSection(sectiondm, section);
2248        DMGetLocalVector(sectiondm, &vec);
2249        PetscObjectSetName((PetscObject)vec, "vec_name");
2250        DMPlexTopologyView(dm, viewer);
2251        DMPlexSectionView(dm, viewer, sectiondm);
2252        DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2253        DMRestoreLocalVector(sectiondm, &vec);
2254        DMDestroy(&sectiondm);
2255        DMDestroy(&dm);
2256 .ve
2257 
2258 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2259 @*/
2260 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2261 {
2262   PetscBool ishdf5;
2263 
2264   PetscFunctionBegin;
2265   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2266   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2267   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2268   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2269   /* Check consistency */
2270   {
2271     PetscSection section;
2272     PetscBool    includesConstraints;
2273     PetscInt     m, m1;
2274 
2275     PetscCall(VecGetLocalSize(vec, &m1));
2276     PetscCall(DMGetLocalSection(sectiondm, &section));
2277     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2278     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2279     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2280     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2281   }
2282   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2283   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2284   if (ishdf5) {
2285 #if defined(PETSC_HAVE_HDF5)
2286     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2287 #else
2288     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2289 #endif
2290   }
2291   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2292   PetscFunctionReturn(PETSC_SUCCESS);
2293 }
2294 
2295 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2296 {
2297   PetscBool ishdf5;
2298 
2299   PetscFunctionBegin;
2300   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2301   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2302   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2303   if (ishdf5) {
2304 #if defined(PETSC_HAVE_HDF5)
2305     PetscViewerFormat format;
2306     PetscCall(PetscViewerGetFormat(viewer, &format));
2307     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2308       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2309     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2310       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2311     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2312     PetscFunctionReturn(PETSC_SUCCESS);
2313 #else
2314     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2315 #endif
2316   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2317 }
2318 
2319 /*@
2320   DMPlexTopologyLoad - Loads a topology into a `DMPLEX`
2321 
2322   Collective
2323 
2324   Input Parameters:
2325 + dm     - The `DM` into which the topology is loaded
2326 - viewer - The `PetscViewer` for the saved topology
2327 
2328   Output Parameter:
2329 . globalToLocalPointSF - The `PetscSF` that pushes points in [0, N) to the associated points in the loaded `DMPLEX`, where N is the global number of points; `NULL` if unneeded
2330 
2331   Level: advanced
2332 
2333 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2334           `PetscViewer`, `PetscSF`
2335 @*/
2336 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2337 {
2338   PetscBool ishdf5;
2339 
2340   PetscFunctionBegin;
2341   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2342   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2343   if (globalToLocalPointSF) PetscAssertPointer(globalToLocalPointSF, 3);
2344   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2345   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2346   if (ishdf5) {
2347 #if defined(PETSC_HAVE_HDF5)
2348     PetscViewerFormat format;
2349     PetscCall(PetscViewerGetFormat(viewer, &format));
2350     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2351       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2352     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2353 #else
2354     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2355 #endif
2356   }
2357   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2358   PetscFunctionReturn(PETSC_SUCCESS);
2359 }
2360 
2361 /*@
2362   DMPlexCoordinatesLoad - Loads coordinates into a `DMPLEX`
2363 
2364   Collective
2365 
2366   Input Parameters:
2367 + dm                   - The `DM` into which the coordinates are loaded
2368 . viewer               - The `PetscViewer` for the saved coordinates
2369 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2370 
2371   Level: advanced
2372 
2373 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2374           `PetscSF`, `PetscViewer`
2375 @*/
2376 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2377 {
2378   PetscBool ishdf5;
2379 
2380   PetscFunctionBegin;
2381   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2382   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2383   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2384   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2385   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2386   if (ishdf5) {
2387 #if defined(PETSC_HAVE_HDF5)
2388     PetscViewerFormat format;
2389     PetscCall(PetscViewerGetFormat(viewer, &format));
2390     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2391       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2392     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2393 #else
2394     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2395 #endif
2396   }
2397   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2398   PetscFunctionReturn(PETSC_SUCCESS);
2399 }
2400 
2401 /*@
2402   DMPlexLabelsLoad - Loads labels into a `DMPLEX`
2403 
2404   Collective
2405 
2406   Input Parameters:
2407 + dm                   - The `DM` into which the labels are loaded
2408 . viewer               - The `PetscViewer` for the saved labels
2409 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading `dm` from viewer
2410 
2411   Level: advanced
2412 
2413   Note:
2414   The `PetscSF` argument must not be NULL if the `DM` is distributed, otherwise an error occurs.
2415 
2416 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2417           `PetscSF`, `PetscViewer`
2418 @*/
2419 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2420 {
2421   PetscBool ishdf5;
2422 
2423   PetscFunctionBegin;
2424   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2425   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2426   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2427   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2428   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2429   if (ishdf5) {
2430 #if defined(PETSC_HAVE_HDF5)
2431     PetscViewerFormat format;
2432 
2433     PetscCall(PetscViewerGetFormat(viewer, &format));
2434     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2435       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2436     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2437 #else
2438     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2439 #endif
2440   }
2441   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2442   PetscFunctionReturn(PETSC_SUCCESS);
2443 }
2444 
2445 /*@
2446   DMPlexSectionLoad - Loads section into a `DMPLEX`
2447 
2448   Collective
2449 
2450   Input Parameters:
2451 + dm                   - The `DM` that represents the topology
2452 . viewer               - The `PetscViewer` that represents the on-disk section (sectionA)
2453 . sectiondm            - The `DM` into which the on-disk section (sectionA) is migrated
2454 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad(`) when loading dm from viewer
2455 
2456   Output Parameters:
2457 + globalDofSF - The `PetscSF` that migrates any on-disk `Vec` data associated with sectionA into a global `Vec` associated with the `sectiondm`'s global section (`NULL` if not needed)
2458 - localDofSF  - The `PetscSF` that migrates any on-disk `Vec` data associated with sectionA into a local `Vec` associated with the `sectiondm`'s local section (`NULL` if not needed)
2459 
2460   Level: advanced
2461 
2462   Notes:
2463   This function is a wrapper around `PetscSectionLoad()`; it loads, in addition to the raw section, a list of global point numbers that associates each on-disk section point with a global point number in [0, NX), where NX is the number of topology points in `dm`. Noting that globalToLocalPointSF associates each topology point in dm with a global number in [0, NX), one can readily establish an association of the on-disk section points with the topology points.
2464 
2465   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2466 
2467   The output parameter, `globalDofSF` (`localDofSF`), can later be used with `DMPlexGlobalVectorLoad()` (`DMPlexLocalVectorLoad()`) to load on-disk vectors into global (local) vectors associated with sectiondm's global (local) section.
2468 
2469   Example using 2 processes:
2470 .vb
2471   NX (number of points on dm): 4
2472   sectionA                   : the on-disk section
2473   vecA                       : a vector associated with sectionA
2474   sectionB                   : sectiondm's local section constructed in this function
2475   vecB (local)               : a vector associated with sectiondm's local section
2476   vecB (global)              : a vector associated with sectiondm's global section
2477 
2478                                      rank 0    rank 1
2479   vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2480   sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2481   sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2482   sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2483   [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2484   sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2485   sectionB->atlasDof             :     1 0 1 | 1 3
2486   sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2487   vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2488   vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2489 .ve
2490   where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2491 
2492 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`, `PetscSF`, `PetscViewer`
2493 @*/
2494 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
2495 {
2496   PetscBool ishdf5;
2497 
2498   PetscFunctionBegin;
2499   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2500   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2501   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2502   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2503   if (globalDofSF) PetscAssertPointer(globalDofSF, 5);
2504   if (localDofSF) PetscAssertPointer(localDofSF, 6);
2505   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2506   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2507   if (ishdf5) {
2508 #if defined(PETSC_HAVE_HDF5)
2509     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2510 #else
2511     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2512 #endif
2513   }
2514   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2515   PetscFunctionReturn(PETSC_SUCCESS);
2516 }
2517 
2518 /*@
2519   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2520 
2521   Collective
2522 
2523   Input Parameters:
2524 + dm        - The `DM` that represents the topology
2525 . viewer    - The `PetscViewer` that represents the on-disk vector data
2526 . sectiondm - The `DM` that contains the global section on which vec is defined
2527 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2528 - vec       - The global vector to set values of
2529 
2530   Level: advanced
2531 
2532   Notes:
2533   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2534 
2535   Calling sequence:
2536 .vb
2537        DMCreate(PETSC_COMM_WORLD, &dm);
2538        DMSetType(dm, DMPLEX);
2539        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2540        DMPlexTopologyLoad(dm, viewer, &sfX);
2541        DMClone(dm, &sectiondm);
2542        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2543        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2544        DMGetGlobalVector(sectiondm, &vec);
2545        PetscObjectSetName((PetscObject)vec, "vec_name");
2546        DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2547        DMRestoreGlobalVector(sectiondm, &vec);
2548        PetscSFDestroy(&gsf);
2549        PetscSFDestroy(&sfX);
2550        DMDestroy(&sectiondm);
2551        DMDestroy(&dm);
2552 .ve
2553 
2554 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2555           `PetscSF`, `PetscViewer`
2556 @*/
2557 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2558 {
2559   PetscBool ishdf5;
2560 
2561   PetscFunctionBegin;
2562   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2563   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2564   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2565   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2566   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2567   /* Check consistency */
2568   {
2569     PetscSection section;
2570     PetscBool    includesConstraints;
2571     PetscInt     m, m1;
2572 
2573     PetscCall(VecGetLocalSize(vec, &m1));
2574     PetscCall(DMGetGlobalSection(sectiondm, &section));
2575     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2576     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2577     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2578     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2579   }
2580   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2581   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2582   if (ishdf5) {
2583 #if defined(PETSC_HAVE_HDF5)
2584     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2585 #else
2586     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2587 #endif
2588   }
2589   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2590   PetscFunctionReturn(PETSC_SUCCESS);
2591 }
2592 
2593 /*@
2594   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2595 
2596   Collective
2597 
2598   Input Parameters:
2599 + dm        - The `DM` that represents the topology
2600 . viewer    - The `PetscViewer` that represents the on-disk vector data
2601 . sectiondm - The `DM` that contains the local section on which vec is defined
2602 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2603 - vec       - The local vector to set values of
2604 
2605   Level: advanced
2606 
2607   Notes:
2608   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2609 
2610   Calling sequence:
2611 .vb
2612        DMCreate(PETSC_COMM_WORLD, &dm);
2613        DMSetType(dm, DMPLEX);
2614        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2615        DMPlexTopologyLoad(dm, viewer, &sfX);
2616        DMClone(dm, &sectiondm);
2617        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2618        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2619        DMGetLocalVector(sectiondm, &vec);
2620        PetscObjectSetName((PetscObject)vec, "vec_name");
2621        DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2622        DMRestoreLocalVector(sectiondm, &vec);
2623        PetscSFDestroy(&lsf);
2624        PetscSFDestroy(&sfX);
2625        DMDestroy(&sectiondm);
2626        DMDestroy(&dm);
2627 .ve
2628 
2629 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2630           `PetscSF`, `PetscViewer`
2631 @*/
2632 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2633 {
2634   PetscBool ishdf5;
2635 
2636   PetscFunctionBegin;
2637   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2638   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2639   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2640   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2641   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2642   /* Check consistency */
2643   {
2644     PetscSection section;
2645     PetscBool    includesConstraints;
2646     PetscInt     m, m1;
2647 
2648     PetscCall(VecGetLocalSize(vec, &m1));
2649     PetscCall(DMGetLocalSection(sectiondm, &section));
2650     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2651     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2652     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2653     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2654   }
2655   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2656   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2657   if (ishdf5) {
2658 #if defined(PETSC_HAVE_HDF5)
2659     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2660 #else
2661     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2662 #endif
2663   }
2664   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2665   PetscFunctionReturn(PETSC_SUCCESS);
2666 }
2667 
2668 PetscErrorCode DMDestroy_Plex(DM dm)
2669 {
2670   DM_Plex *mesh = (DM_Plex *)dm->data;
2671 
2672   PetscFunctionBegin;
2673   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2674   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2675   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2676   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2677   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerviativeBoundaryValues_C", NULL));
2678   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2679   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2680   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2681   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2682   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2683   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2684   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2685   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2686   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetUseCeed_C", NULL));
2687   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetUseCeed_C", NULL));
2688   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", NULL));
2689   if (--mesh->refct > 0) PetscFunctionReturn(PETSC_SUCCESS);
2690   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2691   PetscCall(PetscFree(mesh->cones));
2692   PetscCall(PetscFree(mesh->coneOrientations));
2693   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2694   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2695   PetscCall(PetscFree(mesh->supports));
2696   PetscCall(PetscFree(mesh->cellTypes));
2697   PetscCall(DMPlexTransformDestroy(&mesh->tr));
2698   PetscCall(PetscFree(mesh->tetgenOpts));
2699   PetscCall(PetscFree(mesh->triangleOpts));
2700   PetscCall(PetscFree(mesh->transformType));
2701   PetscCall(PetscFree(mesh->distributionName));
2702   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2703   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2704   PetscCall(ISDestroy(&mesh->subpointIS));
2705   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2706   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2707   PetscCall(PetscSFDestroy(&mesh->periodic.face_sf));
2708   PetscCall(PetscSFDestroy(&mesh->periodic.composed_sf));
2709   PetscCall(ISDestroy(&mesh->periodic.periodic_points));
2710   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2711   PetscCall(ISDestroy(&mesh->anchorIS));
2712   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2713   PetscCall(PetscFree(mesh->parents));
2714   PetscCall(PetscFree(mesh->childIDs));
2715   PetscCall(PetscSectionDestroy(&mesh->childSection));
2716   PetscCall(PetscFree(mesh->children));
2717   PetscCall(DMDestroy(&mesh->referenceTree));
2718   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2719   PetscCall(PetscFree(mesh->neighbors));
2720   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2721   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2722   PetscCall(PetscFree(mesh));
2723   PetscFunctionReturn(PETSC_SUCCESS);
2724 }
2725 
2726 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2727 {
2728   PetscSection           sectionGlobal;
2729   PetscInt               bs = -1, mbs;
2730   PetscInt               localSize, localStart = 0;
2731   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2732   MatType                mtype;
2733   ISLocalToGlobalMapping ltog;
2734 
2735   PetscFunctionBegin;
2736   PetscCall(MatInitializePackage());
2737   mtype = dm->mattype;
2738   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2739   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2740   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2741   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2742   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2743   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2744   PetscCall(MatSetType(*J, mtype));
2745   PetscCall(MatSetFromOptions(*J));
2746   PetscCall(MatGetBlockSize(*J, &mbs));
2747   if (mbs > 1) bs = mbs;
2748   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2749   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2750   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2751   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2752   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2753   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2754   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2755   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2756   if (!isShell) {
2757     PetscBool fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2758     PetscInt *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2759     PetscInt  pStart, pEnd, p, dof, cdof, num_fields;
2760 
2761     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2762 
2763     PetscCall(PetscCalloc1(localSize, &pblocks));
2764     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2765     PetscCall(PetscSectionGetNumFields(sectionGlobal, &num_fields));
2766     for (p = pStart; p < pEnd; ++p) {
2767       switch (dm->blocking_type) {
2768       case DM_BLOCKING_TOPOLOGICAL_POINT: { // One block per topological point
2769         PetscInt bdof, offset;
2770 
2771         PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2772         PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2773         PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2774         for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = dof - cdof;
2775         dof  = dof < 0 ? -(dof + 1) : dof;
2776         bdof = cdof && (dof - cdof) ? 1 : dof;
2777         if (dof) {
2778           if (bs < 0) {
2779             bs = bdof;
2780           } else if (bs != bdof) {
2781             bs = 1;
2782           }
2783         }
2784       } break;
2785       case DM_BLOCKING_FIELD_NODE: {
2786         for (PetscInt field = 0; field < num_fields; field++) {
2787           PetscInt num_comp, bdof, offset;
2788           PetscCall(PetscSectionGetFieldComponents(sectionGlobal, field, &num_comp));
2789           PetscCall(PetscSectionGetFieldDof(sectionGlobal, p, field, &dof));
2790           if (dof < 0) continue;
2791           PetscCall(PetscSectionGetFieldOffset(sectionGlobal, p, field, &offset));
2792           PetscCall(PetscSectionGetFieldConstraintDof(sectionGlobal, p, field, &cdof));
2793           PetscAssert(dof % num_comp == 0, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " field %" PetscInt_FMT " has %" PetscInt_FMT " dof, not divisible by %" PetscInt_FMT " component ", p, field, dof, num_comp);
2794           PetscInt num_nodes = dof / num_comp;
2795           for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = (dof - cdof) / num_nodes;
2796           // Handle possibly constant block size (unlikely)
2797           bdof = cdof && (dof - cdof) ? 1 : dof;
2798           if (dof) {
2799             if (bs < 0) {
2800               bs = bdof;
2801             } else if (bs != bdof) {
2802               bs = 1;
2803             }
2804           }
2805         }
2806       } break;
2807       }
2808     }
2809     /* Must have same blocksize on all procs (some might have no points) */
2810     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2811     bsLocal[1] = bs;
2812     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
2813     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2814     else bs = bsMinMax[0];
2815     bs = PetscMax(1, bs);
2816     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
2817     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2818       PetscCall(MatSetBlockSize(*J, bs));
2819       PetscCall(MatSetUp(*J));
2820     } else {
2821       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
2822       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2823       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2824     }
2825     { // Consolidate blocks
2826       PetscInt nblocks = 0;
2827       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
2828         if (pblocks[i] == 0) continue;
2829         pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2830         for (PetscInt j = 1; j < pblocks[i]; j++) PetscCheck(pblocks[i + j] == pblocks[i], PETSC_COMM_SELF, PETSC_ERR_PLIB, "Block of size %" PetscInt_FMT " mismatches entry %" PetscInt_FMT, pblocks[i], pblocks[i + j]);
2831       }
2832       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2833     }
2834     PetscCall(PetscFree(pblocks));
2835   }
2836   PetscCall(MatSetDM(*J, dm));
2837   PetscFunctionReturn(PETSC_SUCCESS);
2838 }
2839 
2840 /*@
2841   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2842 
2843   Not Collective
2844 
2845   Input Parameter:
2846 . dm - The `DMPLEX`
2847 
2848   Output Parameter:
2849 . subsection - The subdomain section
2850 
2851   Level: developer
2852 
2853 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscSection`
2854 @*/
2855 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2856 {
2857   DM_Plex *mesh = (DM_Plex *)dm->data;
2858 
2859   PetscFunctionBegin;
2860   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2861   if (!mesh->subdomainSection) {
2862     PetscSection section;
2863     PetscSF      sf;
2864 
2865     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
2866     PetscCall(DMGetLocalSection(dm, &section));
2867     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
2868     PetscCall(PetscSFDestroy(&sf));
2869   }
2870   *subsection = mesh->subdomainSection;
2871   PetscFunctionReturn(PETSC_SUCCESS);
2872 }
2873 
2874 /*@
2875   DMPlexGetChart - Return the interval for all mesh points [`pStart`, `pEnd`)
2876 
2877   Not Collective
2878 
2879   Input Parameter:
2880 . dm - The `DMPLEX`
2881 
2882   Output Parameters:
2883 + pStart - The first mesh point
2884 - pEnd   - The upper bound for mesh points
2885 
2886   Level: beginner
2887 
2888 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`
2889 @*/
2890 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
2891 {
2892   DM_Plex *mesh = (DM_Plex *)dm->data;
2893 
2894   PetscFunctionBegin;
2895   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2896   if (mesh->tr) PetscCall(DMPlexTransformGetChart(mesh->tr, pStart, pEnd));
2897   else PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
2898   PetscFunctionReturn(PETSC_SUCCESS);
2899 }
2900 
2901 /*@
2902   DMPlexSetChart - Set the interval for all mesh points [`pStart`, `pEnd`)
2903 
2904   Not Collective
2905 
2906   Input Parameters:
2907 + dm     - The `DMPLEX`
2908 . pStart - The first mesh point
2909 - pEnd   - The upper bound for mesh points
2910 
2911   Level: beginner
2912 
2913 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetChart()`
2914 @*/
2915 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
2916 {
2917   DM_Plex *mesh = (DM_Plex *)dm->data;
2918 
2919   PetscFunctionBegin;
2920   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2921   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
2922   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
2923   PetscCall(PetscFree(mesh->cellTypes));
2924   PetscFunctionReturn(PETSC_SUCCESS);
2925 }
2926 
2927 /*@
2928   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
2929 
2930   Not Collective
2931 
2932   Input Parameters:
2933 + dm - The `DMPLEX`
2934 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
2935 
2936   Output Parameter:
2937 . size - The cone size for point `p`
2938 
2939   Level: beginner
2940 
2941 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
2942 @*/
2943 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
2944 {
2945   DM_Plex *mesh = (DM_Plex *)dm->data;
2946 
2947   PetscFunctionBegin;
2948   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2949   PetscAssertPointer(size, 3);
2950   if (mesh->tr) PetscCall(DMPlexTransformGetConeSize(mesh->tr, p, size));
2951   else PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
2952   PetscFunctionReturn(PETSC_SUCCESS);
2953 }
2954 
2955 /*@
2956   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
2957 
2958   Not Collective
2959 
2960   Input Parameters:
2961 + dm   - The `DMPLEX`
2962 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
2963 - size - The cone size for point `p`
2964 
2965   Level: beginner
2966 
2967   Note:
2968   This should be called after `DMPlexSetChart()`.
2969 
2970 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2971 @*/
2972 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
2973 {
2974   DM_Plex *mesh = (DM_Plex *)dm->data;
2975 
2976   PetscFunctionBegin;
2977   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2978   PetscCheck(!mesh->tr, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Cannot call DMPlexSetConeSize() on a mesh with a transform defined.");
2979   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
2980   PetscFunctionReturn(PETSC_SUCCESS);
2981 }
2982 
2983 /*@C
2984   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
2985 
2986   Not Collective
2987 
2988   Input Parameters:
2989 + dm - The `DMPLEX`
2990 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
2991 
2992   Output Parameter:
2993 . cone - An array of points which are on the in-edges for point `p`
2994 
2995   Level: beginner
2996 
2997   Fortran Notes:
2998   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
2999   `DMPlexRestoreCone()` is not needed/available in C.
3000 
3001 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`, `DMPlexRestoreCone()`
3002 @*/
3003 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
3004 {
3005   DM_Plex *mesh = (DM_Plex *)dm->data;
3006   PetscInt off;
3007 
3008   PetscFunctionBegin;
3009   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3010   PetscAssertPointer(cone, 3);
3011   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3012   *cone = &mesh->cones[off];
3013   PetscFunctionReturn(PETSC_SUCCESS);
3014 }
3015 
3016 /*@C
3017   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
3018 
3019   Not Collective
3020 
3021   Input Parameters:
3022 + dm - The `DMPLEX`
3023 - p  - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3024 
3025   Output Parameters:
3026 + pConesSection - `PetscSection` describing the layout of `pCones`
3027 - pCones        - An array of points which are on the in-edges for the point set `p`
3028 
3029   Level: intermediate
3030 
3031 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`, `PetscSection`, `IS`
3032 @*/
3033 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
3034 {
3035   PetscSection cs, newcs;
3036   PetscInt    *cones;
3037   PetscInt    *newarr = NULL;
3038   PetscInt     n;
3039 
3040   PetscFunctionBegin;
3041   PetscCall(DMPlexGetCones(dm, &cones));
3042   PetscCall(DMPlexGetConeSection(dm, &cs));
3043   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
3044   if (pConesSection) *pConesSection = newcs;
3045   if (pCones) {
3046     PetscCall(PetscSectionGetStorageSize(newcs, &n));
3047     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
3048   }
3049   PetscFunctionReturn(PETSC_SUCCESS);
3050 }
3051 
3052 /*@
3053   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
3054 
3055   Not Collective
3056 
3057   Input Parameters:
3058 + dm     - The `DMPLEX`
3059 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3060 
3061   Output Parameter:
3062 . expandedPoints - An array of vertices recursively expanded from input points
3063 
3064   Level: advanced
3065 
3066   Notes:
3067   Like `DMPlexGetConeRecursive()` but returns only the 0-depth `IS` (i.e. vertices only) and no sections.
3068 
3069   There is no corresponding Restore function, just call `ISDestroy()` on the returned `IS` to deallocate.
3070 
3071 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`,
3072           `DMPlexGetDepth()`, `IS`
3073 @*/
3074 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
3075 {
3076   IS      *expandedPointsAll;
3077   PetscInt depth;
3078 
3079   PetscFunctionBegin;
3080   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3081   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3082   PetscAssertPointer(expandedPoints, 3);
3083   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3084   *expandedPoints = expandedPointsAll[0];
3085   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
3086   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3087   PetscFunctionReturn(PETSC_SUCCESS);
3088 }
3089 
3090 /*@
3091   DMPlexGetConeRecursive - Expand each given point into its cone points and do that recursively until we end up just with vertices (DAG points of depth 0, i.e. without cones).
3092 
3093   Not Collective
3094 
3095   Input Parameters:
3096 + dm     - The `DMPLEX`
3097 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3098 
3099   Output Parameters:
3100 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3101 . expandedPoints - (optional) An array of index sets with recursively expanded cones
3102 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3103 
3104   Level: advanced
3105 
3106   Notes:
3107   Like `DMPlexGetConeTuple()` but recursive.
3108 
3109   Array `expandedPoints` has size equal to `depth`. Each `expandedPoints`[d] contains DAG points with maximum depth d, recursively cone-wise expanded from the input points.
3110   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
3111 
3112   Array section has size equal to `depth`.  Each `PetscSection` `sections`[d] realizes mapping from `expandedPoints`[d+1] (section points) to `expandedPoints`[d] (section dofs) as follows\:
3113   (1) DAG points in `expandedPoints`[d+1] with `depth` d+1 to their cone points in `expandedPoints`[d];
3114   (2) DAG points in `expandedPoints`[d+1] with `depth` in [0,d] to the same points in `expandedPoints`[d].
3115 
3116 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3117           `DMPlexGetDepth()`, `PetscSection`, `IS`
3118 @*/
3119 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3120 {
3121   const PetscInt *arr0 = NULL, *cone = NULL;
3122   PetscInt       *arr = NULL, *newarr = NULL;
3123   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
3124   IS             *expandedPoints_;
3125   PetscSection   *sections_;
3126 
3127   PetscFunctionBegin;
3128   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3129   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3130   if (depth) PetscAssertPointer(depth, 3);
3131   if (expandedPoints) PetscAssertPointer(expandedPoints, 4);
3132   if (sections) PetscAssertPointer(sections, 5);
3133   PetscCall(ISGetLocalSize(points, &n));
3134   PetscCall(ISGetIndices(points, &arr0));
3135   PetscCall(DMPlexGetDepth(dm, &depth_));
3136   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
3137   PetscCall(PetscCalloc1(depth_, &sections_));
3138   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
3139   for (d = depth_ - 1; d >= 0; d--) {
3140     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
3141     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
3142     for (i = 0; i < n; i++) {
3143       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
3144       if (arr[i] >= start && arr[i] < end) {
3145         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
3146         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
3147       } else {
3148         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
3149       }
3150     }
3151     PetscCall(PetscSectionSetUp(sections_[d]));
3152     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
3153     PetscCall(PetscMalloc1(newn, &newarr));
3154     for (i = 0; i < n; i++) {
3155       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
3156       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
3157       if (cn > 1) {
3158         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
3159         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
3160       } else {
3161         newarr[co] = arr[i];
3162       }
3163     }
3164     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
3165     arr = newarr;
3166     n   = newn;
3167   }
3168   PetscCall(ISRestoreIndices(points, &arr0));
3169   *depth = depth_;
3170   if (expandedPoints) *expandedPoints = expandedPoints_;
3171   else {
3172     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
3173     PetscCall(PetscFree(expandedPoints_));
3174   }
3175   if (sections) *sections = sections_;
3176   else {
3177     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
3178     PetscCall(PetscFree(sections_));
3179   }
3180   PetscFunctionReturn(PETSC_SUCCESS);
3181 }
3182 
3183 /*@
3184   DMPlexRestoreConeRecursive - Deallocates arrays created by `DMPlexGetConeRecursive()`
3185 
3186   Not Collective
3187 
3188   Input Parameters:
3189 + dm     - The `DMPLEX`
3190 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3191 
3192   Output Parameters:
3193 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3194 . expandedPoints - (optional) An array of recursively expanded cones
3195 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3196 
3197   Level: advanced
3198 
3199   Note:
3200   See `DMPlexGetConeRecursive()`
3201 
3202 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3203           `DMPlexGetDepth()`, `IS`, `PetscSection`
3204 @*/
3205 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3206 {
3207   PetscInt d, depth_;
3208 
3209   PetscFunctionBegin;
3210   PetscCall(DMPlexGetDepth(dm, &depth_));
3211   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
3212   if (depth) *depth = 0;
3213   if (expandedPoints) {
3214     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
3215     PetscCall(PetscFree(*expandedPoints));
3216   }
3217   if (sections) {
3218     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
3219     PetscCall(PetscFree(*sections));
3220   }
3221   PetscFunctionReturn(PETSC_SUCCESS);
3222 }
3223 
3224 /*@
3225   DMPlexSetCone - Set the points on the in-edges for this point in the DAG; that is these are the points that cover the specific point
3226 
3227   Not Collective
3228 
3229   Input Parameters:
3230 + dm   - The `DMPLEX`
3231 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3232 - cone - An array of points which are on the in-edges for point `p`
3233 
3234   Level: beginner
3235 
3236   Note:
3237   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3238 
3239 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3240 @*/
3241 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3242 {
3243   DM_Plex *mesh = (DM_Plex *)dm->data;
3244   PetscInt dof, off, c;
3245 
3246   PetscFunctionBegin;
3247   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3248   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3249   if (dof) PetscAssertPointer(cone, 3);
3250   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3251   if (PetscDefined(USE_DEBUG)) {
3252     PetscInt pStart, pEnd;
3253     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3254     PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3255     for (c = 0; c < dof; ++c) {
3256       PetscCheck(!(cone[c] < pStart) && !(cone[c] >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", cone[c], pStart, pEnd);
3257       mesh->cones[off + c] = cone[c];
3258     }
3259   } else {
3260     for (c = 0; c < dof; ++c) mesh->cones[off + c] = cone[c];
3261   }
3262   PetscFunctionReturn(PETSC_SUCCESS);
3263 }
3264 
3265 /*@C
3266   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3267 
3268   Not Collective
3269 
3270   Input Parameters:
3271 + dm - The `DMPLEX`
3272 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3273 
3274   Output Parameter:
3275 . coneOrientation - An array of orientations which are on the in-edges for point `p`. An orientation is an
3276                     integer giving the prescription for cone traversal.
3277 
3278   Level: beginner
3279 
3280   Note:
3281   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3282   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3283   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3284   with the identity.
3285 
3286   Fortran Notes:
3287   You must also call `DMPlexRestoreConeOrientation()` after you finish using the returned array.
3288   `DMPlexRestoreConeOrientation()` is not needed/available in C.
3289 
3290 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3291 @*/
3292 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3293 {
3294   DM_Plex *mesh = (DM_Plex *)dm->data;
3295   PetscInt off;
3296 
3297   PetscFunctionBegin;
3298   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3299   if (PetscDefined(USE_DEBUG)) {
3300     PetscInt dof;
3301     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3302     if (dof) PetscAssertPointer(coneOrientation, 3);
3303   }
3304   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3305 
3306   *coneOrientation = &mesh->coneOrientations[off];
3307   PetscFunctionReturn(PETSC_SUCCESS);
3308 }
3309 
3310 /*@
3311   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3312 
3313   Not Collective
3314 
3315   Input Parameters:
3316 + dm              - The `DMPLEX`
3317 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3318 - coneOrientation - An array of orientations
3319 
3320   Level: beginner
3321 
3322   Notes:
3323   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3324 
3325   The meaning of coneOrientation is detailed in `DMPlexGetConeOrientation()`.
3326 
3327 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3328 @*/
3329 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3330 {
3331   DM_Plex *mesh = (DM_Plex *)dm->data;
3332   PetscInt pStart, pEnd;
3333   PetscInt dof, off, c;
3334 
3335   PetscFunctionBegin;
3336   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3337   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3338   if (dof) PetscAssertPointer(coneOrientation, 3);
3339   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3340   if (PetscDefined(USE_DEBUG)) {
3341     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3342     PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3343     for (c = 0; c < dof; ++c) {
3344       PetscInt cdof, o = coneOrientation[c];
3345 
3346       PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3347       PetscCheck(!o || (o >= -(cdof + 1) && o < cdof), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone orientation %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ". %" PetscInt_FMT ")", o, -(cdof + 1), cdof);
3348       mesh->coneOrientations[off + c] = o;
3349     }
3350   } else {
3351     for (c = 0; c < dof; ++c) mesh->coneOrientations[off + c] = coneOrientation[c];
3352   }
3353   PetscFunctionReturn(PETSC_SUCCESS);
3354 }
3355 
3356 /*@
3357   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3358 
3359   Not Collective
3360 
3361   Input Parameters:
3362 + dm        - The `DMPLEX`
3363 . p         - The point, which must lie in the chart set with `DMPlexSetChart()`
3364 . conePos   - The local index in the cone where the point should be put
3365 - conePoint - The mesh point to insert
3366 
3367   Level: beginner
3368 
3369 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3370 @*/
3371 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3372 {
3373   DM_Plex *mesh = (DM_Plex *)dm->data;
3374   PetscInt pStart, pEnd;
3375   PetscInt dof, off;
3376 
3377   PetscFunctionBegin;
3378   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3379   if (PetscDefined(USE_DEBUG)) {
3380     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3381     PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3382     PetscCheck(!(conePoint < pStart) && !(conePoint >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", conePoint, pStart, pEnd);
3383     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3384     PetscCheck(!(conePos < 0) && !(conePos >= dof), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone position %" PetscInt_FMT " of point %" PetscInt_FMT " is not in the valid range [0, %" PetscInt_FMT ")", conePos, p, dof);
3385   }
3386   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3387   mesh->cones[off + conePos] = conePoint;
3388   PetscFunctionReturn(PETSC_SUCCESS);
3389 }
3390 
3391 /*@
3392   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3393 
3394   Not Collective
3395 
3396   Input Parameters:
3397 + dm              - The `DMPLEX`
3398 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3399 . conePos         - The local index in the cone where the point should be put
3400 - coneOrientation - The point orientation to insert
3401 
3402   Level: beginner
3403 
3404   Note:
3405   The meaning of coneOrientation values is detailed in `DMPlexGetConeOrientation()`.
3406 
3407 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3408 @*/
3409 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3410 {
3411   DM_Plex *mesh = (DM_Plex *)dm->data;
3412   PetscInt pStart, pEnd;
3413   PetscInt dof, off;
3414 
3415   PetscFunctionBegin;
3416   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3417   if (PetscDefined(USE_DEBUG)) {
3418     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3419     PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3420     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3421     PetscCheck(!(conePos < 0) && !(conePos >= dof), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone position %" PetscInt_FMT " of point %" PetscInt_FMT " is not in the valid range [0, %" PetscInt_FMT ")", conePos, p, dof);
3422   }
3423   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3424   mesh->coneOrientations[off + conePos] = coneOrientation;
3425   PetscFunctionReturn(PETSC_SUCCESS);
3426 }
3427 
3428 /*@C
3429   DMPlexGetOrientedCone - Return the points and orientations on the in-edges for this point in the DAG
3430 
3431   Not collective
3432 
3433   Input Parameters:
3434 + dm - The DMPlex
3435 - p  - The point, which must lie in the chart set with DMPlexSetChart()
3436 
3437   Output Parameters:
3438 + cone - An array of points which are on the in-edges for point `p`
3439 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3440         integer giving the prescription for cone traversal.
3441 
3442   Level: beginner
3443 
3444   Notes:
3445   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3446   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3447   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3448   with the identity.
3449 
3450   Fortran Notes:
3451   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3452   `DMPlexRestoreCone()` is not needed/available in C.
3453 
3454 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3455 @*/
3456 PetscErrorCode DMPlexGetOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3457 {
3458   DM_Plex *mesh = (DM_Plex *)dm->data;
3459 
3460   PetscFunctionBegin;
3461   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3462   if (mesh->tr) {
3463     PetscCall(DMPlexTransformGetCone(mesh->tr, p, cone, ornt));
3464   } else {
3465     PetscInt off;
3466     if (PetscDefined(USE_DEBUG)) {
3467       PetscInt dof;
3468       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3469       if (dof) {
3470         if (cone) PetscAssertPointer(cone, 3);
3471         if (ornt) PetscAssertPointer(ornt, 4);
3472       }
3473     }
3474     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3475     if (cone) *cone = mesh->cones ? mesh->cones + off : NULL; // NULL + 0 is UB
3476     if (ornt) *ornt = mesh->coneOrientations ? mesh->coneOrientations + off : NULL;
3477   }
3478   PetscFunctionReturn(PETSC_SUCCESS);
3479 }
3480 
3481 /*@C
3482   DMPlexRestoreOrientedCone - Restore the points and orientations on the in-edges for this point in the DAG
3483 
3484   Not Collective
3485 
3486   Input Parameters:
3487 + dm   - The DMPlex
3488 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3489 . cone - An array of points which are on the in-edges for point p
3490 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3491         integer giving the prescription for cone traversal.
3492 
3493   Level: beginner
3494 
3495   Notes:
3496   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3497   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3498   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3499   with the identity.
3500 
3501   Fortran Notes:
3502   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3503   `DMPlexRestoreCone()` is not needed/available in C.
3504 
3505 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3506 @*/
3507 PetscErrorCode DMPlexRestoreOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3508 {
3509   DM_Plex *mesh = (DM_Plex *)dm->data;
3510 
3511   PetscFunctionBegin;
3512   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3513   if (mesh->tr) PetscCall(DMPlexTransformRestoreCone(mesh->tr, p, cone, ornt));
3514   PetscFunctionReturn(PETSC_SUCCESS);
3515 }
3516 
3517 /*@
3518   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3519 
3520   Not Collective
3521 
3522   Input Parameters:
3523 + dm - The `DMPLEX`
3524 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3525 
3526   Output Parameter:
3527 . size - The support size for point `p`
3528 
3529   Level: beginner
3530 
3531 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3532 @*/
3533 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3534 {
3535   DM_Plex *mesh = (DM_Plex *)dm->data;
3536 
3537   PetscFunctionBegin;
3538   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3539   PetscAssertPointer(size, 3);
3540   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3541   PetscFunctionReturn(PETSC_SUCCESS);
3542 }
3543 
3544 /*@
3545   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3546 
3547   Not Collective
3548 
3549   Input Parameters:
3550 + dm   - The `DMPLEX`
3551 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3552 - size - The support size for point `p`
3553 
3554   Level: beginner
3555 
3556   Note:
3557   This should be called after `DMPlexSetChart()`.
3558 
3559 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3560 @*/
3561 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3562 {
3563   DM_Plex *mesh = (DM_Plex *)dm->data;
3564 
3565   PetscFunctionBegin;
3566   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3567   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3568   PetscFunctionReturn(PETSC_SUCCESS);
3569 }
3570 
3571 /*@C
3572   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3573 
3574   Not Collective
3575 
3576   Input Parameters:
3577 + dm - The `DMPLEX`
3578 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3579 
3580   Output Parameter:
3581 . support - An array of points which are on the out-edges for point `p`
3582 
3583   Level: beginner
3584 
3585   Fortran Notes:
3586   You must also call `DMPlexRestoreSupport()` after you finish using the returned array.
3587   `DMPlexRestoreSupport()` is not needed/available in C.
3588 
3589 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3590 @*/
3591 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3592 {
3593   DM_Plex *mesh = (DM_Plex *)dm->data;
3594   PetscInt off;
3595 
3596   PetscFunctionBegin;
3597   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3598   PetscAssertPointer(support, 3);
3599   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3600   *support = mesh->supports ? mesh->supports + off : NULL; //NULL + 0 is UB
3601   PetscFunctionReturn(PETSC_SUCCESS);
3602 }
3603 
3604 /*@
3605   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3606 
3607   Not Collective
3608 
3609   Input Parameters:
3610 + dm      - The `DMPLEX`
3611 . p       - The point, which must lie in the chart set with `DMPlexSetChart()`
3612 - support - An array of points which are on the out-edges for point `p`
3613 
3614   Level: beginner
3615 
3616   Note:
3617   This should be called after all calls to `DMPlexSetSupportSize()` and `DMSetUp()`.
3618 
3619 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3620 @*/
3621 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3622 {
3623   DM_Plex *mesh = (DM_Plex *)dm->data;
3624   PetscInt pStart, pEnd;
3625   PetscInt dof, off, c;
3626 
3627   PetscFunctionBegin;
3628   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3629   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3630   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3631   if (dof) PetscAssertPointer(support, 3);
3632   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3633   PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3634   for (c = 0; c < dof; ++c) {
3635     PetscCheck(!(support[c] < pStart) && !(support[c] >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Support point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", support[c], pStart, pEnd);
3636     mesh->supports[off + c] = support[c];
3637   }
3638   PetscFunctionReturn(PETSC_SUCCESS);
3639 }
3640 
3641 /*@
3642   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3643 
3644   Not Collective
3645 
3646   Input Parameters:
3647 + dm           - The `DMPLEX`
3648 . p            - The point, which must lie in the chart set with `DMPlexSetChart()`
3649 . supportPos   - The local index in the cone where the point should be put
3650 - supportPoint - The mesh point to insert
3651 
3652   Level: beginner
3653 
3654 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3655 @*/
3656 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3657 {
3658   DM_Plex *mesh = (DM_Plex *)dm->data;
3659   PetscInt pStart, pEnd;
3660   PetscInt dof, off;
3661 
3662   PetscFunctionBegin;
3663   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3664   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3665   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3666   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3667   PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3668   PetscCheck(!(supportPoint < pStart) && !(supportPoint >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Support point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", supportPoint, pStart, pEnd);
3669   PetscCheck(supportPos < dof, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Support position %" PetscInt_FMT " of point %" PetscInt_FMT " is not in the valid range [0, %" PetscInt_FMT ")", supportPos, p, dof);
3670   mesh->supports[off + supportPos] = supportPoint;
3671   PetscFunctionReturn(PETSC_SUCCESS);
3672 }
3673 
3674 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3675 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3676 {
3677   switch (ct) {
3678   case DM_POLYTOPE_SEGMENT:
3679     if (o == -1) return -2;
3680     break;
3681   case DM_POLYTOPE_TRIANGLE:
3682     if (o == -3) return -1;
3683     if (o == -2) return -3;
3684     if (o == -1) return -2;
3685     break;
3686   case DM_POLYTOPE_QUADRILATERAL:
3687     if (o == -4) return -2;
3688     if (o == -3) return -1;
3689     if (o == -2) return -4;
3690     if (o == -1) return -3;
3691     break;
3692   default:
3693     return o;
3694   }
3695   return o;
3696 }
3697 
3698 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3699 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3700 {
3701   switch (ct) {
3702   case DM_POLYTOPE_SEGMENT:
3703     if ((o == -2) || (o == 1)) return -1;
3704     if (o == -1) return 0;
3705     break;
3706   case DM_POLYTOPE_TRIANGLE:
3707     if (o == -3) return -2;
3708     if (o == -2) return -1;
3709     if (o == -1) return -3;
3710     break;
3711   case DM_POLYTOPE_QUADRILATERAL:
3712     if (o == -4) return -2;
3713     if (o == -3) return -1;
3714     if (o == -2) return -4;
3715     if (o == -1) return -3;
3716     break;
3717   default:
3718     return o;
3719   }
3720   return o;
3721 }
3722 
3723 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3724 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3725 {
3726   PetscInt pStart, pEnd, p;
3727 
3728   PetscFunctionBegin;
3729   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3730   for (p = pStart; p < pEnd; ++p) {
3731     const PetscInt *cone, *ornt;
3732     PetscInt        coneSize, c;
3733 
3734     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3735     PetscCall(DMPlexGetCone(dm, p, &cone));
3736     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3737     for (c = 0; c < coneSize; ++c) {
3738       DMPolytopeType ct;
3739       const PetscInt o = ornt[c];
3740 
3741       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3742       switch (ct) {
3743       case DM_POLYTOPE_SEGMENT:
3744         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3745         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3746         break;
3747       case DM_POLYTOPE_TRIANGLE:
3748         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3749         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3750         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3751         break;
3752       case DM_POLYTOPE_QUADRILATERAL:
3753         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3754         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3755         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3756         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3757         break;
3758       default:
3759         break;
3760       }
3761     }
3762   }
3763   PetscFunctionReturn(PETSC_SUCCESS);
3764 }
3765 
3766 static inline PetscErrorCode DMPlexGetTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3767 {
3768   DM_Plex *mesh = (DM_Plex *)dm->data;
3769 
3770   PetscFunctionBeginHot;
3771   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3772     if (useCone) {
3773       PetscCall(DMPlexGetConeSize(dm, p, size));
3774       PetscCall(DMPlexGetOrientedCone(dm, p, arr, ornt));
3775     } else {
3776       PetscCall(DMPlexGetSupportSize(dm, p, size));
3777       PetscCall(DMPlexGetSupport(dm, p, arr));
3778     }
3779   } else {
3780     if (useCone) {
3781       const PetscSection s   = mesh->coneSection;
3782       const PetscInt     ps  = p - s->pStart;
3783       const PetscInt     off = s->atlasOff[ps];
3784 
3785       *size = s->atlasDof[ps];
3786       *arr  = mesh->cones + off;
3787       *ornt = mesh->coneOrientations + off;
3788     } else {
3789       const PetscSection s   = mesh->supportSection;
3790       const PetscInt     ps  = p - s->pStart;
3791       const PetscInt     off = s->atlasOff[ps];
3792 
3793       *size = s->atlasDof[ps];
3794       *arr  = mesh->supports + off;
3795     }
3796   }
3797   PetscFunctionReturn(PETSC_SUCCESS);
3798 }
3799 
3800 static inline PetscErrorCode DMPlexRestoreTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3801 {
3802   DM_Plex *mesh = (DM_Plex *)dm->data;
3803 
3804   PetscFunctionBeginHot;
3805   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3806     if (useCone) PetscCall(DMPlexRestoreOrientedCone(dm, p, arr, ornt));
3807   }
3808   PetscFunctionReturn(PETSC_SUCCESS);
3809 }
3810 
3811 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3812 {
3813   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3814   PetscInt       *closure;
3815   const PetscInt *tmp = NULL, *tmpO = NULL;
3816   PetscInt        off = 0, tmpSize, t;
3817 
3818   PetscFunctionBeginHot;
3819   if (ornt) {
3820     PetscCall(DMPlexGetCellType(dm, p, &ct));
3821     if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3822   }
3823   if (*points) {
3824     closure = *points;
3825   } else {
3826     PetscInt maxConeSize, maxSupportSize;
3827     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3828     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
3829   }
3830   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3831   if (ct == DM_POLYTOPE_UNKNOWN) {
3832     closure[off++] = p;
3833     closure[off++] = 0;
3834     for (t = 0; t < tmpSize; ++t) {
3835       closure[off++] = tmp[t];
3836       closure[off++] = tmpO ? tmpO[t] : 0;
3837     }
3838   } else {
3839     const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, ornt);
3840 
3841     /* We assume that cells with a valid type have faces with a valid type */
3842     closure[off++] = p;
3843     closure[off++] = ornt;
3844     for (t = 0; t < tmpSize; ++t) {
3845       DMPolytopeType ft;
3846 
3847       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3848       closure[off++] = tmp[arr[t]];
3849       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3850     }
3851   }
3852   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3853   if (numPoints) *numPoints = tmpSize + 1;
3854   if (points) *points = closure;
3855   PetscFunctionReturn(PETSC_SUCCESS);
3856 }
3857 
3858 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
3859 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3860 {
3861   const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, o);
3862   const PetscInt *cone, *ornt;
3863   PetscInt       *pts, *closure = NULL;
3864   DMPolytopeType  ft;
3865   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3866   PetscInt        dim, coneSize, c, d, clSize, cl;
3867 
3868   PetscFunctionBeginHot;
3869   PetscCall(DMGetDimension(dm, &dim));
3870   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3871   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3872   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
3873   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
3874   maxSize       = PetscMax(coneSeries, supportSeries);
3875   if (*points) {
3876     pts = *points;
3877   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
3878   c        = 0;
3879   pts[c++] = point;
3880   pts[c++] = o;
3881   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
3882   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
3883   for (cl = 0; cl < clSize * 2; cl += 2) {
3884     pts[c++] = closure[cl];
3885     pts[c++] = closure[cl + 1];
3886   }
3887   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
3888   for (cl = 0; cl < clSize * 2; cl += 2) {
3889     pts[c++] = closure[cl];
3890     pts[c++] = closure[cl + 1];
3891   }
3892   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3893   for (d = 2; d < coneSize; ++d) {
3894     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
3895     pts[c++] = cone[arr[d * 2 + 0]];
3896     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
3897   }
3898   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3899   if (dim >= 3) {
3900     for (d = 2; d < coneSize; ++d) {
3901       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
3902       const PetscInt *fcone, *fornt;
3903       PetscInt        fconeSize, fc, i;
3904 
3905       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
3906       const PetscInt *farr = DMPolytopeTypeGetArrangment(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
3907       PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
3908       for (fc = 0; fc < fconeSize; ++fc) {
3909         const PetscInt cp = fcone[farr[fc * 2 + 0]];
3910         const PetscInt co = farr[fc * 2 + 1];
3911 
3912         for (i = 0; i < c; i += 2)
3913           if (pts[i] == cp) break;
3914         if (i == c) {
3915           PetscCall(DMPlexGetCellType(dm, cp, &ft));
3916           pts[c++] = cp;
3917           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
3918         }
3919       }
3920       PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
3921     }
3922   }
3923   *numPoints = c / 2;
3924   *points    = pts;
3925   PetscFunctionReturn(PETSC_SUCCESS);
3926 }
3927 
3928 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3929 {
3930   DMPolytopeType ct;
3931   PetscInt      *closure, *fifo;
3932   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
3933   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
3934   PetscInt       depth, maxSize;
3935 
3936   PetscFunctionBeginHot;
3937   PetscCall(DMPlexGetDepth(dm, &depth));
3938   if (depth == 1) {
3939     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
3940     PetscFunctionReturn(PETSC_SUCCESS);
3941   }
3942   PetscCall(DMPlexGetCellType(dm, p, &ct));
3943   if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3944   if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
3945     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
3946     PetscFunctionReturn(PETSC_SUCCESS);
3947   }
3948   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3949   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
3950   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
3951   maxSize       = PetscMax(coneSeries, supportSeries);
3952   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3953   if (*points) {
3954     closure = *points;
3955   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
3956   closure[closureSize++] = p;
3957   closure[closureSize++] = ornt;
3958   fifo[fifoSize++]       = p;
3959   fifo[fifoSize++]       = ornt;
3960   fifo[fifoSize++]       = ct;
3961   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
3962   while (fifoSize - fifoStart) {
3963     const PetscInt       q    = fifo[fifoStart++];
3964     const PetscInt       o    = fifo[fifoStart++];
3965     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
3966     const PetscInt      *qarr = DMPolytopeTypeGetArrangment(qt, o);
3967     const PetscInt      *tmp, *tmpO = NULL;
3968     PetscInt             tmpSize, t;
3969 
3970     if (PetscDefined(USE_DEBUG)) {
3971       PetscInt nO = DMPolytopeTypeGetNumArrangments(qt) / 2;
3972       PetscCheck(!o || !(o >= nO || o < -nO), PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid orientation %" PetscInt_FMT " not in [%" PetscInt_FMT ",%" PetscInt_FMT ") for %s %" PetscInt_FMT, o, -nO, nO, DMPolytopeTypes[qt], q);
3973     }
3974     PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
3975     for (t = 0; t < tmpSize; ++t) {
3976       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
3977       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
3978       const PetscInt cp = tmp[ip];
3979       PetscCall(DMPlexGetCellType(dm, cp, &ct));
3980       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
3981       PetscInt       c;
3982 
3983       /* Check for duplicate */
3984       for (c = 0; c < closureSize; c += 2) {
3985         if (closure[c] == cp) break;
3986       }
3987       if (c == closureSize) {
3988         closure[closureSize++] = cp;
3989         closure[closureSize++] = co;
3990         fifo[fifoSize++]       = cp;
3991         fifo[fifoSize++]       = co;
3992         fifo[fifoSize++]       = ct;
3993       }
3994     }
3995     PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
3996   }
3997   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3998   if (numPoints) *numPoints = closureSize / 2;
3999   if (points) *points = closure;
4000   PetscFunctionReturn(PETSC_SUCCESS);
4001 }
4002 
4003 /*@C
4004   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
4005 
4006   Not Collective
4007 
4008   Input Parameters:
4009 + dm      - The `DMPLEX`
4010 . p       - The mesh point
4011 - useCone - `PETSC_TRUE` for the closure, otherwise return the star
4012 
4013   Input/Output Parameter:
4014 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
4015            if `NULL` on input, internal storage will be returned, otherwise the provided array is used
4016 
4017   Output Parameter:
4018 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
4019 
4020   Level: beginner
4021 
4022   Note:
4023   If using internal storage (points is `NULL` on input), each call overwrites the last output.
4024 
4025   Fortran Notes:
4026   The `numPoints` argument is not present in the Fortran binding since it is internal to the array.
4027 
4028 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4029 @*/
4030 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4031 {
4032   PetscFunctionBeginHot;
4033   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4034   if (numPoints) PetscAssertPointer(numPoints, 4);
4035   if (points) PetscAssertPointer(points, 5);
4036   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
4037   PetscFunctionReturn(PETSC_SUCCESS);
4038 }
4039 
4040 /*@C
4041   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
4042 
4043   Not Collective
4044 
4045   Input Parameters:
4046 + dm        - The `DMPLEX`
4047 . p         - The mesh point
4048 . useCone   - `PETSC_TRUE` for the closure, otherwise return the star
4049 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
4050 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
4051 
4052   Level: beginner
4053 
4054   Note:
4055   If not using internal storage (points is not `NULL` on input), this call is unnecessary
4056 
4057 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4058 @*/
4059 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4060 {
4061   PetscFunctionBeginHot;
4062   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4063   if (numPoints) *numPoints = 0;
4064   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
4065   PetscFunctionReturn(PETSC_SUCCESS);
4066 }
4067 
4068 /*@
4069   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
4070 
4071   Not Collective
4072 
4073   Input Parameter:
4074 . dm - The `DMPLEX`
4075 
4076   Output Parameters:
4077 + maxConeSize    - The maximum number of in-edges
4078 - maxSupportSize - The maximum number of out-edges
4079 
4080   Level: beginner
4081 
4082 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
4083 @*/
4084 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
4085 {
4086   DM_Plex *mesh = (DM_Plex *)dm->data;
4087 
4088   PetscFunctionBegin;
4089   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4090   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
4091   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
4092   PetscFunctionReturn(PETSC_SUCCESS);
4093 }
4094 
4095 PetscErrorCode DMSetUp_Plex(DM dm)
4096 {
4097   DM_Plex *mesh = (DM_Plex *)dm->data;
4098   PetscInt size, maxSupportSize;
4099 
4100   PetscFunctionBegin;
4101   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4102   PetscCall(PetscSectionSetUp(mesh->coneSection));
4103   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
4104   PetscCall(PetscMalloc1(size, &mesh->cones));
4105   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
4106   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4107   if (maxSupportSize) {
4108     PetscCall(PetscSectionSetUp(mesh->supportSection));
4109     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
4110     PetscCall(PetscMalloc1(size, &mesh->supports));
4111   }
4112   PetscFunctionReturn(PETSC_SUCCESS);
4113 }
4114 
4115 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
4116 {
4117   PetscFunctionBegin;
4118   if (subdm) PetscCall(DMClone(dm, subdm));
4119   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, is, subdm));
4120   if (subdm) (*subdm)->useNatural = dm->useNatural;
4121   if (dm->useNatural && dm->sfMigration) {
4122     PetscSF sfNatural;
4123 
4124     (*subdm)->sfMigration = dm->sfMigration;
4125     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
4126     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
4127     (*subdm)->sfNatural = sfNatural;
4128   }
4129   PetscFunctionReturn(PETSC_SUCCESS);
4130 }
4131 
4132 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
4133 {
4134   PetscInt i = 0;
4135 
4136   PetscFunctionBegin;
4137   PetscCall(DMClone(dms[0], superdm));
4138   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
4139   (*superdm)->useNatural = PETSC_FALSE;
4140   for (i = 0; i < len; i++) {
4141     if (dms[i]->useNatural && dms[i]->sfMigration) {
4142       PetscSF sfNatural;
4143 
4144       (*superdm)->sfMigration = dms[i]->sfMigration;
4145       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
4146       (*superdm)->useNatural = PETSC_TRUE;
4147       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
4148       (*superdm)->sfNatural = sfNatural;
4149       break;
4150     }
4151   }
4152   PetscFunctionReturn(PETSC_SUCCESS);
4153 }
4154 
4155 /*@
4156   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
4157 
4158   Not Collective
4159 
4160   Input Parameter:
4161 . dm - The `DMPLEX`
4162 
4163   Level: beginner
4164 
4165   Note:
4166   This should be called after all calls to `DMPlexSetCone()`
4167 
4168 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
4169 @*/
4170 PetscErrorCode DMPlexSymmetrize(DM dm)
4171 {
4172   DM_Plex  *mesh = (DM_Plex *)dm->data;
4173   PetscInt *offsets;
4174   PetscInt  supportSize;
4175   PetscInt  pStart, pEnd, p;
4176 
4177   PetscFunctionBegin;
4178   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4179   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
4180   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
4181   /* Calculate support sizes */
4182   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4183   for (p = pStart; p < pEnd; ++p) {
4184     PetscInt dof, off, c;
4185 
4186     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4187     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4188     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
4189   }
4190   PetscCall(PetscSectionSetUp(mesh->supportSection));
4191   /* Calculate supports */
4192   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
4193   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
4194   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
4195   for (p = pStart; p < pEnd; ++p) {
4196     PetscInt dof, off, c;
4197 
4198     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4199     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4200     for (c = off; c < off + dof; ++c) {
4201       const PetscInt q = mesh->cones[c];
4202       PetscInt       offS;
4203 
4204       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
4205 
4206       mesh->supports[offS + offsets[q]] = p;
4207       ++offsets[q];
4208     }
4209   }
4210   PetscCall(PetscFree(offsets));
4211   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
4212   PetscFunctionReturn(PETSC_SUCCESS);
4213 }
4214 
4215 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
4216 {
4217   IS stratumIS;
4218 
4219   PetscFunctionBegin;
4220   if (pStart >= pEnd) PetscFunctionReturn(PETSC_SUCCESS);
4221   if (PetscDefined(USE_DEBUG)) {
4222     PetscInt  qStart, qEnd, numLevels, level;
4223     PetscBool overlap = PETSC_FALSE;
4224     PetscCall(DMLabelGetNumValues(label, &numLevels));
4225     for (level = 0; level < numLevels; level++) {
4226       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4227       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
4228         overlap = PETSC_TRUE;
4229         break;
4230       }
4231     }
4232     PetscCheck(!overlap, PETSC_COMM_SELF, PETSC_ERR_PLIB, "New depth %" PetscInt_FMT " range [%" PetscInt_FMT ",%" PetscInt_FMT ") overlaps with depth %" PetscInt_FMT " range [%" PetscInt_FMT ",%" PetscInt_FMT ")", depth, pStart, pEnd, level, qStart, qEnd);
4233   }
4234   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
4235   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
4236   PetscCall(ISDestroy(&stratumIS));
4237   PetscFunctionReturn(PETSC_SUCCESS);
4238 }
4239 
4240 /*@
4241   DMPlexStratify - Computes the strata for all points in the `DMPLEX`
4242 
4243   Collective
4244 
4245   Input Parameter:
4246 . dm - The `DMPLEX`
4247 
4248   Level: beginner
4249 
4250   Notes:
4251   The strata group all points of the same grade, and this function calculates the strata. This
4252   grade can be seen as the height (or depth) of the point in the DAG.
4253 
4254   The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
4255   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram).
4256   Concretely, `DMPlexStratify()` creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
4257   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
4258   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through `DMPlexGetDepthLabel()` or `DMPlexGetDepthStratum()`, or
4259   manually via `DMGetLabel()`.  The height is defined implicitly by height = maxDimension - depth, and can be accessed
4260   via `DMPlexGetHeightStratum()`.  For example, cells have height 0 and faces have height 1.
4261 
4262   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
4263   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
4264   we had a mesh consisting of one triangle (c0) and three vertices (v0, v1, v2), and only one edge is on the boundary so we choose
4265   to interpolate only that one (e0), so that
4266 .vb
4267   cone(c0) = {e0, v2}
4268   cone(e0) = {v0, v1}
4269 .ve
4270   If `DMPlexStratify()` is run on this mesh, it will give depths
4271 .vb
4272    depth 0 = {v0, v1, v2}
4273    depth 1 = {e0, c0}
4274 .ve
4275   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
4276 
4277   `DMPlexStratify()` should be called after all calls to `DMPlexSymmetrize()`
4278 
4279 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
4280 @*/
4281 PetscErrorCode DMPlexStratify(DM dm)
4282 {
4283   DM_Plex *mesh = (DM_Plex *)dm->data;
4284   DMLabel  label;
4285   PetscInt pStart, pEnd, p;
4286   PetscInt numRoots = 0, numLeaves = 0;
4287 
4288   PetscFunctionBegin;
4289   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4290   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
4291 
4292   /* Create depth label */
4293   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4294   PetscCall(DMCreateLabel(dm, "depth"));
4295   PetscCall(DMPlexGetDepthLabel(dm, &label));
4296 
4297   {
4298     /* Initialize roots and count leaves */
4299     PetscInt sMin = PETSC_MAX_INT;
4300     PetscInt sMax = PETSC_MIN_INT;
4301     PetscInt coneSize, supportSize;
4302 
4303     for (p = pStart; p < pEnd; ++p) {
4304       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4305       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4306       if (!coneSize && supportSize) {
4307         sMin = PetscMin(p, sMin);
4308         sMax = PetscMax(p, sMax);
4309         ++numRoots;
4310       } else if (!supportSize && coneSize) {
4311         ++numLeaves;
4312       } else if (!supportSize && !coneSize) {
4313         /* Isolated points */
4314         sMin = PetscMin(p, sMin);
4315         sMax = PetscMax(p, sMax);
4316       }
4317     }
4318     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4319   }
4320 
4321   if (numRoots + numLeaves == (pEnd - pStart)) {
4322     PetscInt sMin = PETSC_MAX_INT;
4323     PetscInt sMax = PETSC_MIN_INT;
4324     PetscInt coneSize, supportSize;
4325 
4326     for (p = pStart; p < pEnd; ++p) {
4327       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4328       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4329       if (!supportSize && coneSize) {
4330         sMin = PetscMin(p, sMin);
4331         sMax = PetscMax(p, sMax);
4332       }
4333     }
4334     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4335   } else {
4336     PetscInt level = 0;
4337     PetscInt qStart, qEnd, q;
4338 
4339     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4340     while (qEnd > qStart) {
4341       PetscInt sMin = PETSC_MAX_INT;
4342       PetscInt sMax = PETSC_MIN_INT;
4343 
4344       for (q = qStart; q < qEnd; ++q) {
4345         const PetscInt *support;
4346         PetscInt        supportSize, s;
4347 
4348         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4349         PetscCall(DMPlexGetSupport(dm, q, &support));
4350         for (s = 0; s < supportSize; ++s) {
4351           sMin = PetscMin(support[s], sMin);
4352           sMax = PetscMax(support[s], sMax);
4353         }
4354       }
4355       PetscCall(DMLabelGetNumValues(label, &level));
4356       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4357       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4358     }
4359   }
4360   { /* just in case there is an empty process */
4361     PetscInt numValues, maxValues = 0, v;
4362 
4363     PetscCall(DMLabelGetNumValues(label, &numValues));
4364     PetscCall(MPIU_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4365     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4366   }
4367   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4368   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4369   PetscFunctionReturn(PETSC_SUCCESS);
4370 }
4371 
4372 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4373 {
4374   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4375   PetscInt       dim, depth, pheight, coneSize;
4376 
4377   PetscFunctionBeginHot;
4378   PetscCall(DMGetDimension(dm, &dim));
4379   PetscCall(DMPlexGetDepth(dm, &depth));
4380   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4381   pheight = depth - pdepth;
4382   if (depth <= 1) {
4383     switch (pdepth) {
4384     case 0:
4385       ct = DM_POLYTOPE_POINT;
4386       break;
4387     case 1:
4388       switch (coneSize) {
4389       case 2:
4390         ct = DM_POLYTOPE_SEGMENT;
4391         break;
4392       case 3:
4393         ct = DM_POLYTOPE_TRIANGLE;
4394         break;
4395       case 4:
4396         switch (dim) {
4397         case 2:
4398           ct = DM_POLYTOPE_QUADRILATERAL;
4399           break;
4400         case 3:
4401           ct = DM_POLYTOPE_TETRAHEDRON;
4402           break;
4403         default:
4404           break;
4405         }
4406         break;
4407       case 5:
4408         ct = DM_POLYTOPE_PYRAMID;
4409         break;
4410       case 6:
4411         ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4412         break;
4413       case 8:
4414         ct = DM_POLYTOPE_HEXAHEDRON;
4415         break;
4416       default:
4417         break;
4418       }
4419     }
4420   } else {
4421     if (pdepth == 0) {
4422       ct = DM_POLYTOPE_POINT;
4423     } else if (pheight == 0) {
4424       switch (dim) {
4425       case 1:
4426         switch (coneSize) {
4427         case 2:
4428           ct = DM_POLYTOPE_SEGMENT;
4429           break;
4430         default:
4431           break;
4432         }
4433         break;
4434       case 2:
4435         switch (coneSize) {
4436         case 3:
4437           ct = DM_POLYTOPE_TRIANGLE;
4438           break;
4439         case 4:
4440           ct = DM_POLYTOPE_QUADRILATERAL;
4441           break;
4442         default:
4443           break;
4444         }
4445         break;
4446       case 3:
4447         switch (coneSize) {
4448         case 4:
4449           ct = DM_POLYTOPE_TETRAHEDRON;
4450           break;
4451         case 5: {
4452           const PetscInt *cone;
4453           PetscInt        faceConeSize;
4454 
4455           PetscCall(DMPlexGetCone(dm, p, &cone));
4456           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4457           switch (faceConeSize) {
4458           case 3:
4459             ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4460             break;
4461           case 4:
4462             ct = DM_POLYTOPE_PYRAMID;
4463             break;
4464           }
4465         } break;
4466         case 6:
4467           ct = DM_POLYTOPE_HEXAHEDRON;
4468           break;
4469         default:
4470           break;
4471         }
4472         break;
4473       default:
4474         break;
4475       }
4476     } else if (pheight > 0) {
4477       switch (coneSize) {
4478       case 2:
4479         ct = DM_POLYTOPE_SEGMENT;
4480         break;
4481       case 3:
4482         ct = DM_POLYTOPE_TRIANGLE;
4483         break;
4484       case 4:
4485         ct = DM_POLYTOPE_QUADRILATERAL;
4486         break;
4487       default:
4488         break;
4489       }
4490     }
4491   }
4492   *pt = ct;
4493   PetscFunctionReturn(PETSC_SUCCESS);
4494 }
4495 
4496 /*@
4497   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4498 
4499   Collective
4500 
4501   Input Parameter:
4502 . dm - The `DMPLEX`
4503 
4504   Level: developer
4505 
4506   Note:
4507   This function is normally called automatically when a cell type is requested. It creates an
4508   internal `DMLabel` named "celltype" which can be directly accessed using `DMGetLabel()`. A user may disable
4509   automatic creation by creating the label manually, using `DMCreateLabel`(dm, "celltype").
4510 
4511   `DMPlexComputeCellTypes()` should be called after all calls to `DMPlexSymmetrize()` and `DMPlexStratify()`
4512 
4513 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4514 @*/
4515 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4516 {
4517   DM_Plex *mesh;
4518   DMLabel  ctLabel;
4519   PetscInt pStart, pEnd, p;
4520 
4521   PetscFunctionBegin;
4522   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4523   mesh = (DM_Plex *)dm->data;
4524   PetscCall(DMCreateLabel(dm, "celltype"));
4525   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4526   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4527   PetscCall(PetscFree(mesh->cellTypes));
4528   PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
4529   for (p = pStart; p < pEnd; ++p) {
4530     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4531     PetscInt       pdepth;
4532 
4533     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4534     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4535     PetscCheck(ct != DM_POLYTOPE_UNKNOWN, PETSC_COMM_SELF, PETSC_ERR_SUP, "Point %" PetscInt_FMT " is screwed up", p);
4536     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4537     mesh->cellTypes[p - pStart].value_as_uint8 = ct;
4538   }
4539   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4540   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4541   PetscFunctionReturn(PETSC_SUCCESS);
4542 }
4543 
4544 /*@C
4545   DMPlexGetJoin - Get an array for the join of the set of points
4546 
4547   Not Collective
4548 
4549   Input Parameters:
4550 + dm        - The `DMPLEX` object
4551 . numPoints - The number of input points for the join
4552 - points    - The input points
4553 
4554   Output Parameters:
4555 + numCoveredPoints - The number of points in the join
4556 - coveredPoints    - The points in the join
4557 
4558   Level: intermediate
4559 
4560   Note:
4561   Currently, this is restricted to a single level join
4562 
4563   Fortran Notes:
4564   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4565 
4566 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4567 @*/
4568 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4569 {
4570   DM_Plex  *mesh = (DM_Plex *)dm->data;
4571   PetscInt *join[2];
4572   PetscInt  joinSize, i = 0;
4573   PetscInt  dof, off, p, c, m;
4574   PetscInt  maxSupportSize;
4575 
4576   PetscFunctionBegin;
4577   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4578   PetscAssertPointer(points, 3);
4579   PetscAssertPointer(numCoveredPoints, 4);
4580   PetscAssertPointer(coveredPoints, 5);
4581   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4582   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4583   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4584   /* Copy in support of first point */
4585   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4586   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4587   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4588   /* Check each successive support */
4589   for (p = 1; p < numPoints; ++p) {
4590     PetscInt newJoinSize = 0;
4591 
4592     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4593     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4594     for (c = 0; c < dof; ++c) {
4595       const PetscInt point = mesh->supports[off + c];
4596 
4597       for (m = 0; m < joinSize; ++m) {
4598         if (point == join[i][m]) {
4599           join[1 - i][newJoinSize++] = point;
4600           break;
4601         }
4602       }
4603     }
4604     joinSize = newJoinSize;
4605     i        = 1 - i;
4606   }
4607   *numCoveredPoints = joinSize;
4608   *coveredPoints    = join[i];
4609   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4610   PetscFunctionReturn(PETSC_SUCCESS);
4611 }
4612 
4613 /*@C
4614   DMPlexRestoreJoin - Restore an array for the join of the set of points
4615 
4616   Not Collective
4617 
4618   Input Parameters:
4619 + dm        - The `DMPLEX` object
4620 . numPoints - The number of input points for the join
4621 - points    - The input points
4622 
4623   Output Parameters:
4624 + numCoveredPoints - The number of points in the join
4625 - coveredPoints    - The points in the join
4626 
4627   Level: intermediate
4628 
4629   Fortran Notes:
4630   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4631 
4632 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4633 @*/
4634 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4635 {
4636   PetscFunctionBegin;
4637   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4638   if (points) PetscAssertPointer(points, 3);
4639   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4640   PetscAssertPointer(coveredPoints, 5);
4641   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4642   if (numCoveredPoints) *numCoveredPoints = 0;
4643   PetscFunctionReturn(PETSC_SUCCESS);
4644 }
4645 
4646 /*@C
4647   DMPlexGetFullJoin - Get an array for the join of the set of points
4648 
4649   Not Collective
4650 
4651   Input Parameters:
4652 + dm        - The `DMPLEX` object
4653 . numPoints - The number of input points for the join
4654 - points    - The input points
4655 
4656   Output Parameters:
4657 + numCoveredPoints - The number of points in the join
4658 - coveredPoints    - The points in the join
4659 
4660   Level: intermediate
4661 
4662   Fortran Notes:
4663   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4664 
4665 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4666 @*/
4667 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4668 {
4669   PetscInt *offsets, **closures;
4670   PetscInt *join[2];
4671   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4672   PetscInt  p, d, c, m, ms;
4673 
4674   PetscFunctionBegin;
4675   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4676   PetscAssertPointer(points, 3);
4677   PetscAssertPointer(numCoveredPoints, 4);
4678   PetscAssertPointer(coveredPoints, 5);
4679 
4680   PetscCall(DMPlexGetDepth(dm, &depth));
4681   PetscCall(PetscCalloc1(numPoints, &closures));
4682   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4683   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4684   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4685   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4686   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4687 
4688   for (p = 0; p < numPoints; ++p) {
4689     PetscInt closureSize;
4690 
4691     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4692 
4693     offsets[p * (depth + 2) + 0] = 0;
4694     for (d = 0; d < depth + 1; ++d) {
4695       PetscInt pStart, pEnd, i;
4696 
4697       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4698       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4699         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4700           offsets[p * (depth + 2) + d + 1] = i;
4701           break;
4702         }
4703       }
4704       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4705     }
4706     PetscCheck(offsets[p * (depth + 2) + depth + 1] == closureSize, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Total size of closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[p * (depth + 2) + depth + 1], closureSize);
4707   }
4708   for (d = 0; d < depth + 1; ++d) {
4709     PetscInt dof;
4710 
4711     /* Copy in support of first point */
4712     dof = offsets[d + 1] - offsets[d];
4713     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4714     /* Check each successive cone */
4715     for (p = 1; p < numPoints && joinSize; ++p) {
4716       PetscInt newJoinSize = 0;
4717 
4718       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
4719       for (c = 0; c < dof; ++c) {
4720         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
4721 
4722         for (m = 0; m < joinSize; ++m) {
4723           if (point == join[i][m]) {
4724             join[1 - i][newJoinSize++] = point;
4725             break;
4726           }
4727         }
4728       }
4729       joinSize = newJoinSize;
4730       i        = 1 - i;
4731     }
4732     if (joinSize) break;
4733   }
4734   *numCoveredPoints = joinSize;
4735   *coveredPoints    = join[i];
4736   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4737   PetscCall(PetscFree(closures));
4738   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4739   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
4740   PetscFunctionReturn(PETSC_SUCCESS);
4741 }
4742 
4743 /*@C
4744   DMPlexGetMeet - Get an array for the meet of the set of points
4745 
4746   Not Collective
4747 
4748   Input Parameters:
4749 + dm        - The `DMPLEX` object
4750 . numPoints - The number of input points for the meet
4751 - points    - The input points
4752 
4753   Output Parameters:
4754 + numCoveringPoints - The number of points in the meet
4755 - coveringPoints    - The points in the meet
4756 
4757   Level: intermediate
4758 
4759   Note:
4760   Currently, this is restricted to a single level meet
4761 
4762   Fortran Notes:
4763   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4764 
4765 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4766 @*/
4767 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt **coveringPoints)
4768 {
4769   DM_Plex  *mesh = (DM_Plex *)dm->data;
4770   PetscInt *meet[2];
4771   PetscInt  meetSize, i = 0;
4772   PetscInt  dof, off, p, c, m;
4773   PetscInt  maxConeSize;
4774 
4775   PetscFunctionBegin;
4776   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4777   PetscAssertPointer(points, 3);
4778   PetscAssertPointer(numCoveringPoints, 4);
4779   PetscAssertPointer(coveringPoints, 5);
4780   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4781   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4782   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4783   /* Copy in cone of first point */
4784   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4785   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4786   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
4787   /* Check each successive cone */
4788   for (p = 1; p < numPoints; ++p) {
4789     PetscInt newMeetSize = 0;
4790 
4791     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4792     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4793     for (c = 0; c < dof; ++c) {
4794       const PetscInt point = mesh->cones[off + c];
4795 
4796       for (m = 0; m < meetSize; ++m) {
4797         if (point == meet[i][m]) {
4798           meet[1 - i][newMeetSize++] = point;
4799           break;
4800         }
4801       }
4802     }
4803     meetSize = newMeetSize;
4804     i        = 1 - i;
4805   }
4806   *numCoveringPoints = meetSize;
4807   *coveringPoints    = meet[i];
4808   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
4809   PetscFunctionReturn(PETSC_SUCCESS);
4810 }
4811 
4812 /*@C
4813   DMPlexRestoreMeet - Restore an array for the meet of the set of points
4814 
4815   Not Collective
4816 
4817   Input Parameters:
4818 + dm        - The `DMPLEX` object
4819 . numPoints - The number of input points for the meet
4820 - points    - The input points
4821 
4822   Output Parameters:
4823 + numCoveredPoints - The number of points in the meet
4824 - coveredPoints    - The points in the meet
4825 
4826   Level: intermediate
4827 
4828   Fortran Notes:
4829   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4830 
4831 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
4832 @*/
4833 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4834 {
4835   PetscFunctionBegin;
4836   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4837   if (points) PetscAssertPointer(points, 3);
4838   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4839   PetscAssertPointer(coveredPoints, 5);
4840   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4841   if (numCoveredPoints) *numCoveredPoints = 0;
4842   PetscFunctionReturn(PETSC_SUCCESS);
4843 }
4844 
4845 /*@C
4846   DMPlexGetFullMeet - Get an array for the meet of the set of points
4847 
4848   Not Collective
4849 
4850   Input Parameters:
4851 + dm        - The `DMPLEX` object
4852 . numPoints - The number of input points for the meet
4853 - points    - The input points
4854 
4855   Output Parameters:
4856 + numCoveredPoints - The number of points in the meet
4857 - coveredPoints    - The points in the meet
4858 
4859   Level: intermediate
4860 
4861   Fortran Notes:
4862   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4863 
4864 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4865 @*/
4866 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4867 {
4868   PetscInt *offsets, **closures;
4869   PetscInt *meet[2];
4870   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
4871   PetscInt  p, h, c, m, mc;
4872 
4873   PetscFunctionBegin;
4874   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4875   PetscAssertPointer(points, 3);
4876   PetscAssertPointer(numCoveredPoints, 4);
4877   PetscAssertPointer(coveredPoints, 5);
4878 
4879   PetscCall(DMPlexGetDepth(dm, &height));
4880   PetscCall(PetscMalloc1(numPoints, &closures));
4881   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4882   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
4883   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
4884   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
4885   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
4886 
4887   for (p = 0; p < numPoints; ++p) {
4888     PetscInt closureSize;
4889 
4890     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
4891 
4892     offsets[p * (height + 2) + 0] = 0;
4893     for (h = 0; h < height + 1; ++h) {
4894       PetscInt pStart, pEnd, i;
4895 
4896       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
4897       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
4898         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4899           offsets[p * (height + 2) + h + 1] = i;
4900           break;
4901         }
4902       }
4903       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
4904     }
4905     PetscCheck(offsets[p * (height + 2) + height + 1] == closureSize, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Total size of closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[p * (height + 2) + height + 1], closureSize);
4906   }
4907   for (h = 0; h < height + 1; ++h) {
4908     PetscInt dof;
4909 
4910     /* Copy in cone of first point */
4911     dof = offsets[h + 1] - offsets[h];
4912     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
4913     /* Check each successive cone */
4914     for (p = 1; p < numPoints && meetSize; ++p) {
4915       PetscInt newMeetSize = 0;
4916 
4917       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
4918       for (c = 0; c < dof; ++c) {
4919         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
4920 
4921         for (m = 0; m < meetSize; ++m) {
4922           if (point == meet[i][m]) {
4923             meet[1 - i][newMeetSize++] = point;
4924             break;
4925           }
4926         }
4927       }
4928       meetSize = newMeetSize;
4929       i        = 1 - i;
4930     }
4931     if (meetSize) break;
4932   }
4933   *numCoveredPoints = meetSize;
4934   *coveredPoints    = meet[i];
4935   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
4936   PetscCall(PetscFree(closures));
4937   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4938   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
4939   PetscFunctionReturn(PETSC_SUCCESS);
4940 }
4941 
4942 /*@C
4943   DMPlexEqual - Determine if two `DM` have the same topology
4944 
4945   Not Collective
4946 
4947   Input Parameters:
4948 + dmA - A `DMPLEX` object
4949 - dmB - A `DMPLEX` object
4950 
4951   Output Parameter:
4952 . equal - `PETSC_TRUE` if the topologies are identical
4953 
4954   Level: intermediate
4955 
4956   Note:
4957   We are not solving graph isomorphism, so we do not permute.
4958 
4959 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
4960 @*/
4961 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
4962 {
4963   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
4964 
4965   PetscFunctionBegin;
4966   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
4967   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
4968   PetscAssertPointer(equal, 3);
4969 
4970   *equal = PETSC_FALSE;
4971   PetscCall(DMPlexGetDepth(dmA, &depth));
4972   PetscCall(DMPlexGetDepth(dmB, &depthB));
4973   if (depth != depthB) PetscFunctionReturn(PETSC_SUCCESS);
4974   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
4975   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
4976   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(PETSC_SUCCESS);
4977   for (p = pStart; p < pEnd; ++p) {
4978     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
4979     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
4980 
4981     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
4982     PetscCall(DMPlexGetCone(dmA, p, &cone));
4983     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
4984     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
4985     PetscCall(DMPlexGetCone(dmB, p, &coneB));
4986     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
4987     if (coneSize != coneSizeB) PetscFunctionReturn(PETSC_SUCCESS);
4988     for (c = 0; c < coneSize; ++c) {
4989       if (cone[c] != coneB[c]) PetscFunctionReturn(PETSC_SUCCESS);
4990       if (ornt[c] != orntB[c]) PetscFunctionReturn(PETSC_SUCCESS);
4991     }
4992     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
4993     PetscCall(DMPlexGetSupport(dmA, p, &support));
4994     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
4995     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
4996     if (supportSize != supportSizeB) PetscFunctionReturn(PETSC_SUCCESS);
4997     for (s = 0; s < supportSize; ++s) {
4998       if (support[s] != supportB[s]) PetscFunctionReturn(PETSC_SUCCESS);
4999     }
5000   }
5001   *equal = PETSC_TRUE;
5002   PetscFunctionReturn(PETSC_SUCCESS);
5003 }
5004 
5005 /*@C
5006   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
5007 
5008   Not Collective
5009 
5010   Input Parameters:
5011 + dm         - The `DMPLEX`
5012 . cellDim    - The cell dimension
5013 - numCorners - The number of vertices on a cell
5014 
5015   Output Parameter:
5016 . numFaceVertices - The number of vertices on a face
5017 
5018   Level: developer
5019 
5020   Note:
5021   Of course this can only work for a restricted set of symmetric shapes
5022 
5023 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5024 @*/
5025 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
5026 {
5027   MPI_Comm comm;
5028 
5029   PetscFunctionBegin;
5030   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
5031   PetscAssertPointer(numFaceVertices, 4);
5032   switch (cellDim) {
5033   case 0:
5034     *numFaceVertices = 0;
5035     break;
5036   case 1:
5037     *numFaceVertices = 1;
5038     break;
5039   case 2:
5040     switch (numCorners) {
5041     case 3:                 /* triangle */
5042       *numFaceVertices = 2; /* Edge has 2 vertices */
5043       break;
5044     case 4:                 /* quadrilateral */
5045       *numFaceVertices = 2; /* Edge has 2 vertices */
5046       break;
5047     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
5048       *numFaceVertices = 3; /* Edge has 3 vertices */
5049       break;
5050     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
5051       *numFaceVertices = 3; /* Edge has 3 vertices */
5052       break;
5053     default:
5054       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5055     }
5056     break;
5057   case 3:
5058     switch (numCorners) {
5059     case 4:                 /* tetradehdron */
5060       *numFaceVertices = 3; /* Face has 3 vertices */
5061       break;
5062     case 6:                 /* tet cohesive cells */
5063       *numFaceVertices = 4; /* Face has 4 vertices */
5064       break;
5065     case 8:                 /* hexahedron */
5066       *numFaceVertices = 4; /* Face has 4 vertices */
5067       break;
5068     case 9:                 /* tet cohesive Lagrange cells */
5069       *numFaceVertices = 6; /* Face has 6 vertices */
5070       break;
5071     case 10:                /* quadratic tetrahedron */
5072       *numFaceVertices = 6; /* Face has 6 vertices */
5073       break;
5074     case 12:                /* hex cohesive Lagrange cells */
5075       *numFaceVertices = 6; /* Face has 6 vertices */
5076       break;
5077     case 18:                /* quadratic tet cohesive Lagrange cells */
5078       *numFaceVertices = 6; /* Face has 6 vertices */
5079       break;
5080     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
5081       *numFaceVertices = 9; /* Face has 9 vertices */
5082       break;
5083     default:
5084       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5085     }
5086     break;
5087   default:
5088     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
5089   }
5090   PetscFunctionReturn(PETSC_SUCCESS);
5091 }
5092 
5093 /*@
5094   DMPlexGetDepthLabel - Get the `DMLabel` recording the depth of each point
5095 
5096   Not Collective
5097 
5098   Input Parameter:
5099 . dm - The `DMPLEX` object
5100 
5101   Output Parameter:
5102 . depthLabel - The `DMLabel` recording point depth
5103 
5104   Level: developer
5105 
5106 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
5107 @*/
5108 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
5109 {
5110   PetscFunctionBegin;
5111   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5112   PetscAssertPointer(depthLabel, 2);
5113   *depthLabel = dm->depthLabel;
5114   PetscFunctionReturn(PETSC_SUCCESS);
5115 }
5116 
5117 /*@
5118   DMPlexGetDepth - Get the depth of the DAG representing this mesh
5119 
5120   Not Collective
5121 
5122   Input Parameter:
5123 . dm - The `DMPLEX` object
5124 
5125   Output Parameter:
5126 . depth - The number of strata (breadth first levels) in the DAG
5127 
5128   Level: developer
5129 
5130   Notes:
5131   This returns maximum of point depths over all points, i.e. maximum value of the label returned by `DMPlexGetDepthLabel()`.
5132 
5133   The point depth is described more in detail in `DMPlexGetDepthStratum()`.
5134 
5135   An empty mesh gives -1.
5136 
5137 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
5138 @*/
5139 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
5140 {
5141   DM_Plex *mesh = (DM_Plex *)dm->data;
5142   DMLabel  label;
5143   PetscInt d = 0;
5144 
5145   PetscFunctionBegin;
5146   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5147   PetscAssertPointer(depth, 2);
5148   if (mesh->tr) {
5149     PetscCall(DMPlexTransformGetDepth(mesh->tr, depth));
5150   } else {
5151     PetscCall(DMPlexGetDepthLabel(dm, &label));
5152     if (label) PetscCall(DMLabelGetNumValues(label, &d));
5153     *depth = d - 1;
5154   }
5155   PetscFunctionReturn(PETSC_SUCCESS);
5156 }
5157 
5158 /*@
5159   DMPlexGetDepthStratum - Get the bounds [`start`, `end`) for all points at a certain depth.
5160 
5161   Not Collective
5162 
5163   Input Parameters:
5164 + dm    - The `DMPLEX` object
5165 - depth - The requested depth
5166 
5167   Output Parameters:
5168 + start - The first point at this `depth`
5169 - end   - One beyond the last point at this `depth`
5170 
5171   Level: developer
5172 
5173   Notes:
5174   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
5175   often "vertices".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then depth stratum 1 contains the next
5176   higher dimension, e.g., "edges".
5177 
5178 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetHeightStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
5179 @*/
5180 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
5181 {
5182   DM_Plex *mesh = (DM_Plex *)dm->data;
5183   DMLabel  label;
5184   PetscInt pStart, pEnd;
5185 
5186   PetscFunctionBegin;
5187   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5188   if (start) {
5189     PetscAssertPointer(start, 3);
5190     *start = 0;
5191   }
5192   if (end) {
5193     PetscAssertPointer(end, 4);
5194     *end = 0;
5195   }
5196   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5197   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5198   if (depth < 0) {
5199     if (start) *start = pStart;
5200     if (end) *end = pEnd;
5201     PetscFunctionReturn(PETSC_SUCCESS);
5202   }
5203   if (mesh->tr) {
5204     PetscCall(DMPlexTransformGetDepthStratum(mesh->tr, depth, start, end));
5205   } else {
5206     PetscCall(DMPlexGetDepthLabel(dm, &label));
5207     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5208     PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
5209   }
5210   PetscFunctionReturn(PETSC_SUCCESS);
5211 }
5212 
5213 /*@
5214   DMPlexGetHeightStratum - Get the bounds [`start`, `end`) for all points at a certain height.
5215 
5216   Not Collective
5217 
5218   Input Parameters:
5219 + dm     - The `DMPLEX` object
5220 - height - The requested height
5221 
5222   Output Parameters:
5223 + start - The first point at this `height`
5224 - end   - One beyond the last point at this `height`
5225 
5226   Level: developer
5227 
5228   Notes:
5229   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
5230   points, often called "cells" or "elements".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then height
5231   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
5232 
5233 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5234 @*/
5235 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
5236 {
5237   DMLabel  label;
5238   PetscInt depth, pStart, pEnd;
5239 
5240   PetscFunctionBegin;
5241   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5242   if (start) {
5243     PetscAssertPointer(start, 3);
5244     *start = 0;
5245   }
5246   if (end) {
5247     PetscAssertPointer(end, 4);
5248     *end = 0;
5249   }
5250   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5251   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5252   if (height < 0) {
5253     if (start) *start = pStart;
5254     if (end) *end = pEnd;
5255     PetscFunctionReturn(PETSC_SUCCESS);
5256   }
5257   PetscCall(DMPlexGetDepthLabel(dm, &label));
5258   if (label) PetscCall(DMLabelGetNumValues(label, &depth));
5259   else PetscCall(DMGetDimension(dm, &depth));
5260   PetscCheck(depth >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Depth not yet computed");
5261   PetscCall(DMPlexGetDepthStratum(dm, depth - 1 - height, start, end));
5262   PetscFunctionReturn(PETSC_SUCCESS);
5263 }
5264 
5265 /*@
5266   DMPlexGetPointDepth - Get the `depth` of a given point
5267 
5268   Not Collective
5269 
5270   Input Parameters:
5271 + dm    - The `DMPLEX` object
5272 - point - The point
5273 
5274   Output Parameter:
5275 . depth - The depth of the `point`
5276 
5277   Level: intermediate
5278 
5279 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5280 @*/
5281 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
5282 {
5283   PetscFunctionBegin;
5284   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5285   PetscAssertPointer(depth, 3);
5286   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
5287   PetscFunctionReturn(PETSC_SUCCESS);
5288 }
5289 
5290 /*@
5291   DMPlexGetPointHeight - Get the `height` of a given point
5292 
5293   Not Collective
5294 
5295   Input Parameters:
5296 + dm    - The `DMPLEX` object
5297 - point - The point
5298 
5299   Output Parameter:
5300 . height - The height of the `point`
5301 
5302   Level: intermediate
5303 
5304 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
5305 @*/
5306 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5307 {
5308   PetscInt n, pDepth;
5309 
5310   PetscFunctionBegin;
5311   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5312   PetscAssertPointer(height, 3);
5313   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5314   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5315   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5316   PetscFunctionReturn(PETSC_SUCCESS);
5317 }
5318 
5319 /*@
5320   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5321 
5322   Not Collective
5323 
5324   Input Parameter:
5325 . dm - The `DMPLEX` object
5326 
5327   Output Parameter:
5328 . celltypeLabel - The `DMLabel` recording cell polytope type
5329 
5330   Level: developer
5331 
5332   Note:
5333   This function will trigger automatica computation of cell types. This can be disabled by calling
5334   `DMCreateLabel`(dm, "celltype") beforehand.
5335 
5336 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5337 @*/
5338 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5339 {
5340   PetscFunctionBegin;
5341   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5342   PetscAssertPointer(celltypeLabel, 2);
5343   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5344   *celltypeLabel = dm->celltypeLabel;
5345   PetscFunctionReturn(PETSC_SUCCESS);
5346 }
5347 
5348 /*@
5349   DMPlexGetCellType - Get the polytope type of a given cell
5350 
5351   Not Collective
5352 
5353   Input Parameters:
5354 + dm   - The `DMPLEX` object
5355 - cell - The cell
5356 
5357   Output Parameter:
5358 . celltype - The polytope type of the cell
5359 
5360   Level: intermediate
5361 
5362 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeType`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5363 @*/
5364 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5365 {
5366   DM_Plex *mesh = (DM_Plex *)dm->data;
5367   DMLabel  label;
5368   PetscInt ct;
5369 
5370   PetscFunctionBegin;
5371   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5372   PetscAssertPointer(celltype, 3);
5373   if (mesh->tr) {
5374     PetscCall(DMPlexTransformGetCellType(mesh->tr, cell, celltype));
5375   } else {
5376     PetscInt pStart, pEnd;
5377 
5378     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, NULL));
5379     if (!mesh->cellTypes) { /* XXX remove? optimize? */
5380       PetscCall(PetscSectionGetChart(mesh->coneSection, NULL, &pEnd));
5381       PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5382       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5383       for (PetscInt p = pStart; p < pEnd; p++) {
5384         PetscCall(DMLabelGetValue(label, p, &ct));
5385         mesh->cellTypes[p - pStart].value_as_uint8 = (DMPolytopeType)ct;
5386       }
5387     }
5388     *celltype = (DMPolytopeType)mesh->cellTypes[cell - pStart].value_as_uint8;
5389     if (PetscDefined(USE_DEBUG)) {
5390       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5391       PetscCall(DMLabelGetValue(label, cell, &ct));
5392       PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5393       PetscCheck(ct == (PetscInt)*celltype, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid cellType for %" PetscInt_FMT ": %d != %" PetscInt_FMT, cell, (int)*celltype, ct);
5394     }
5395   }
5396   PetscFunctionReturn(PETSC_SUCCESS);
5397 }
5398 
5399 /*@
5400   DMPlexSetCellType - Set the polytope type of a given cell
5401 
5402   Not Collective
5403 
5404   Input Parameters:
5405 + dm       - The `DMPLEX` object
5406 . cell     - The cell
5407 - celltype - The polytope type of the cell
5408 
5409   Level: advanced
5410 
5411   Note:
5412   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5413   is executed. This function will override the computed type. However, if automatic classification will not succeed
5414   and a user wants to manually specify all types, the classification must be disabled by calling
5415   DMCreateLabel(dm, "celltype") before getting or setting any cell types.
5416 
5417 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5418 @*/
5419 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5420 {
5421   DM_Plex *mesh = (DM_Plex *)dm->data;
5422   DMLabel  label;
5423   PetscInt pStart, pEnd;
5424 
5425   PetscFunctionBegin;
5426   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5427   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
5428   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5429   PetscCall(DMLabelSetValue(label, cell, celltype));
5430   if (!mesh->cellTypes) PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5431   mesh->cellTypes[cell - pStart].value_as_uint8 = celltype;
5432   PetscFunctionReturn(PETSC_SUCCESS);
5433 }
5434 
5435 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5436 {
5437   PetscSection section, s;
5438   Mat          m;
5439   PetscInt     maxHeight;
5440   const char  *prefix;
5441 
5442   PetscFunctionBegin;
5443   PetscCall(DMClone(dm, cdm));
5444   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5445   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5446   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5447   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5448   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5449   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5450   PetscCall(DMSetLocalSection(*cdm, section));
5451   PetscCall(PetscSectionDestroy(&section));
5452   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &s));
5453   PetscCall(MatCreate(PETSC_COMM_SELF, &m));
5454   PetscCall(DMSetDefaultConstraints(*cdm, s, m, NULL));
5455   PetscCall(PetscSectionDestroy(&s));
5456   PetscCall(MatDestroy(&m));
5457 
5458   PetscCall(DMSetNumFields(*cdm, 1));
5459   PetscCall(DMCreateDS(*cdm));
5460   (*cdm)->cloneOpts = PETSC_TRUE;
5461   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5462   PetscFunctionReturn(PETSC_SUCCESS);
5463 }
5464 
5465 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5466 {
5467   Vec coordsLocal, cellCoordsLocal;
5468   DM  coordsDM, cellCoordsDM;
5469 
5470   PetscFunctionBegin;
5471   *field = NULL;
5472   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5473   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5474   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5475   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5476   if (coordsLocal && coordsDM) {
5477     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5478     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5479   }
5480   PetscFunctionReturn(PETSC_SUCCESS);
5481 }
5482 
5483 /*@C
5484   DMPlexGetConeSection - Return a section which describes the layout of cone data
5485 
5486   Not Collective
5487 
5488   Input Parameter:
5489 . dm - The `DMPLEX` object
5490 
5491   Output Parameter:
5492 . section - The `PetscSection` object
5493 
5494   Level: developer
5495 
5496 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5497 @*/
5498 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5499 {
5500   DM_Plex *mesh = (DM_Plex *)dm->data;
5501 
5502   PetscFunctionBegin;
5503   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5504   if (section) *section = mesh->coneSection;
5505   PetscFunctionReturn(PETSC_SUCCESS);
5506 }
5507 
5508 /*@C
5509   DMPlexGetSupportSection - Return a section which describes the layout of support data
5510 
5511   Not Collective
5512 
5513   Input Parameter:
5514 . dm - The `DMPLEX` object
5515 
5516   Output Parameter:
5517 . section - The `PetscSection` object
5518 
5519   Level: developer
5520 
5521 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5522 @*/
5523 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5524 {
5525   DM_Plex *mesh = (DM_Plex *)dm->data;
5526 
5527   PetscFunctionBegin;
5528   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5529   if (section) *section = mesh->supportSection;
5530   PetscFunctionReturn(PETSC_SUCCESS);
5531 }
5532 
5533 /*@C
5534   DMPlexGetCones - Return cone data
5535 
5536   Not Collective
5537 
5538   Input Parameter:
5539 . dm - The `DMPLEX` object
5540 
5541   Output Parameter:
5542 . cones - The cone for each point
5543 
5544   Level: developer
5545 
5546 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5547 @*/
5548 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5549 {
5550   DM_Plex *mesh = (DM_Plex *)dm->data;
5551 
5552   PetscFunctionBegin;
5553   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5554   if (cones) *cones = mesh->cones;
5555   PetscFunctionReturn(PETSC_SUCCESS);
5556 }
5557 
5558 /*@C
5559   DMPlexGetConeOrientations - Return cone orientation data
5560 
5561   Not Collective
5562 
5563   Input Parameter:
5564 . dm - The `DMPLEX` object
5565 
5566   Output Parameter:
5567 . coneOrientations - The array of cone orientations for all points
5568 
5569   Level: developer
5570 
5571   Notes:
5572   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points as returned by `DMPlexGetConeOrientation()`.
5573 
5574   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5575 
5576 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5577 @*/
5578 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5579 {
5580   DM_Plex *mesh = (DM_Plex *)dm->data;
5581 
5582   PetscFunctionBegin;
5583   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5584   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5585   PetscFunctionReturn(PETSC_SUCCESS);
5586 }
5587 
5588 /******************************** FEM Support **********************************/
5589 
5590 PetscErrorCode DMPlexGetAllCells_Internal(DM plex, IS *cellIS)
5591 {
5592   PetscInt depth;
5593 
5594   PetscFunctionBegin;
5595   PetscCall(DMPlexGetDepth(plex, &depth));
5596   PetscCall(DMGetStratumIS(plex, "dim", depth, cellIS));
5597   if (!*cellIS) PetscCall(DMGetStratumIS(plex, "depth", depth, cellIS));
5598   PetscFunctionReturn(PETSC_SUCCESS);
5599 }
5600 
5601 PetscErrorCode DMPlexGetAllFaces_Internal(DM plex, IS *faceIS)
5602 {
5603   PetscInt depth;
5604 
5605   PetscFunctionBegin;
5606   PetscCall(DMPlexGetDepth(plex, &depth));
5607   PetscCall(DMGetStratumIS(plex, "dim", depth - 1, faceIS));
5608   if (!*faceIS) PetscCall(DMGetStratumIS(plex, "depth", depth - 1, faceIS));
5609   PetscFunctionReturn(PETSC_SUCCESS);
5610 }
5611 
5612 /*
5613  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5614  representing a line in the section.
5615 */
5616 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(PetscSection section, PetscInt field, PetscInt line, PetscBool vertexchart, PetscInt *Nc, PetscInt *k)
5617 {
5618   PetscFunctionBeginHot;
5619   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5620   if (line < 0) {
5621     *k  = 0;
5622     *Nc = 0;
5623   } else if (vertexchart) { /* If we only have a vertex chart, we must have degree k=1 */
5624     *k = 1;
5625   } else { /* Assume the full interpolated mesh is in the chart; lines in particular */
5626     /* An order k SEM disc has k-1 dofs on an edge */
5627     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5628     *k = *k / *Nc + 1;
5629   }
5630   PetscFunctionReturn(PETSC_SUCCESS);
5631 }
5632 
5633 /*@
5634 
5635   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5636   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5637   section provided (or the section of the `DM`).
5638 
5639   Input Parameters:
5640 + dm      - The `DM`
5641 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or `PETSC_DETERMINE`
5642 - section - The `PetscSection` to reorder, or `NULL` for the default section
5643 
5644   Example:
5645   A typical interpolated single-quad mesh might order points as
5646 .vb
5647   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5648 
5649   v4 -- e6 -- v3
5650   |           |
5651   e7    c0    e8
5652   |           |
5653   v1 -- e5 -- v2
5654 .ve
5655 
5656   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5657   dofs in the order of points, e.g.,
5658 .vb
5659     c0 -> [0,1,2,3]
5660     v1 -> [4]
5661     ...
5662     e5 -> [8, 9]
5663 .ve
5664 
5665   which corresponds to the dofs
5666 .vb
5667     6   10  11  7
5668     13  2   3   15
5669     12  0   1   14
5670     4   8   9   5
5671 .ve
5672 
5673   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5674 .vb
5675   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5676 .ve
5677 
5678   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5679 .vb
5680    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5681 .ve
5682 
5683   Level: developer
5684 
5685   Notes:
5686   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5687   degree of the basis.
5688 
5689   This is required to run with libCEED.
5690 
5691 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5692 @*/
5693 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5694 {
5695   DMLabel   label;
5696   PetscInt  dim, depth = -1, eStart = -1, Nf;
5697   PetscBool vertexchart;
5698 
5699   PetscFunctionBegin;
5700   PetscCall(DMGetDimension(dm, &dim));
5701   if (dim < 1) PetscFunctionReturn(PETSC_SUCCESS);
5702   if (point < 0) {
5703     PetscInt sStart, sEnd;
5704 
5705     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5706     point = sEnd - sStart ? sStart : point;
5707   }
5708   PetscCall(DMPlexGetDepthLabel(dm, &label));
5709   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5710   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5711   if (depth == 1) {
5712     eStart = point;
5713   } else if (depth == dim) {
5714     const PetscInt *cone;
5715 
5716     PetscCall(DMPlexGetCone(dm, point, &cone));
5717     if (dim == 2) eStart = cone[0];
5718     else if (dim == 3) {
5719       const PetscInt *cone2;
5720       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5721       eStart = cone2[0];
5722     } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Point %" PetscInt_FMT " of depth %" PetscInt_FMT " cannot be used to bootstrap spectral ordering for dim %" PetscInt_FMT, point, depth, dim);
5723   } else PetscCheck(depth < 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Point %" PetscInt_FMT " of depth %" PetscInt_FMT " cannot be used to bootstrap spectral ordering for dim %" PetscInt_FMT, point, depth, dim);
5724   { /* Determine whether the chart covers all points or just vertices. */
5725     PetscInt pStart, pEnd, cStart, cEnd;
5726     PetscCall(DMPlexGetDepthStratum(dm, 0, &pStart, &pEnd));
5727     PetscCall(PetscSectionGetChart(section, &cStart, &cEnd));
5728     if (pStart == cStart && pEnd == cEnd) vertexchart = PETSC_TRUE;      /* Only vertices are in the chart */
5729     else if (cStart <= point && point < cEnd) vertexchart = PETSC_FALSE; /* Some interpolated points exist in the chart */
5730     else vertexchart = PETSC_TRUE;                                       /* Some interpolated points are not in chart; assume dofs only at cells and vertices */
5731   }
5732   PetscCall(PetscSectionGetNumFields(section, &Nf));
5733   for (PetscInt d = 1; d <= dim; d++) {
5734     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5735     PetscInt *perm;
5736 
5737     for (f = 0; f < Nf; ++f) {
5738       PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5739       size += PetscPowInt(k + 1, d) * Nc;
5740     }
5741     PetscCall(PetscMalloc1(size, &perm));
5742     for (f = 0; f < Nf; ++f) {
5743       switch (d) {
5744       case 1:
5745         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5746         /*
5747          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5748          We want              [ vtx0; edge of length k-1; vtx1 ]
5749          */
5750         for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
5751         for (i = 0; i < k - 1; i++)
5752           for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
5753         for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
5754         foffset = offset;
5755         break;
5756       case 2:
5757         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5758         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5759         /* The SEM order is
5760 
5761          v_lb, {e_b}, v_rb,
5762          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5763          v_lt, reverse {e_t}, v_rt
5764          */
5765         {
5766           const PetscInt of   = 0;
5767           const PetscInt oeb  = of + PetscSqr(k - 1);
5768           const PetscInt oer  = oeb + (k - 1);
5769           const PetscInt oet  = oer + (k - 1);
5770           const PetscInt oel  = oet + (k - 1);
5771           const PetscInt ovlb = oel + (k - 1);
5772           const PetscInt ovrb = ovlb + 1;
5773           const PetscInt ovrt = ovrb + 1;
5774           const PetscInt ovlt = ovrt + 1;
5775           PetscInt       o;
5776 
5777           /* bottom */
5778           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
5779           for (o = oeb; o < oer; ++o)
5780             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5781           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
5782           /* middle */
5783           for (i = 0; i < k - 1; ++i) {
5784             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
5785             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
5786               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5787             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
5788           }
5789           /* top */
5790           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
5791           for (o = oel - 1; o >= oet; --o)
5792             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5793           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
5794           foffset = offset;
5795         }
5796         break;
5797       case 3:
5798         /* The original hex closure is
5799 
5800          {c,
5801          f_b, f_t, f_f, f_b, f_r, f_l,
5802          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
5803          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
5804          */
5805         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5806         /* The SEM order is
5807          Bottom Slice
5808          v_blf, {e^{(k-1)-n}_bf}, v_brf,
5809          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
5810          v_blb, {e_bb}, v_brb,
5811 
5812          Middle Slice (j)
5813          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
5814          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
5815          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
5816 
5817          Top Slice
5818          v_tlf, {e_tf}, v_trf,
5819          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
5820          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
5821          */
5822         {
5823           const PetscInt oc    = 0;
5824           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
5825           const PetscInt oft   = ofb + PetscSqr(k - 1);
5826           const PetscInt off   = oft + PetscSqr(k - 1);
5827           const PetscInt ofk   = off + PetscSqr(k - 1);
5828           const PetscInt ofr   = ofk + PetscSqr(k - 1);
5829           const PetscInt ofl   = ofr + PetscSqr(k - 1);
5830           const PetscInt oebl  = ofl + PetscSqr(k - 1);
5831           const PetscInt oebb  = oebl + (k - 1);
5832           const PetscInt oebr  = oebb + (k - 1);
5833           const PetscInt oebf  = oebr + (k - 1);
5834           const PetscInt oetf  = oebf + (k - 1);
5835           const PetscInt oetr  = oetf + (k - 1);
5836           const PetscInt oetb  = oetr + (k - 1);
5837           const PetscInt oetl  = oetb + (k - 1);
5838           const PetscInt oerf  = oetl + (k - 1);
5839           const PetscInt oelf  = oerf + (k - 1);
5840           const PetscInt oelb  = oelf + (k - 1);
5841           const PetscInt oerb  = oelb + (k - 1);
5842           const PetscInt ovblf = oerb + (k - 1);
5843           const PetscInt ovblb = ovblf + 1;
5844           const PetscInt ovbrb = ovblb + 1;
5845           const PetscInt ovbrf = ovbrb + 1;
5846           const PetscInt ovtlf = ovbrf + 1;
5847           const PetscInt ovtrf = ovtlf + 1;
5848           const PetscInt ovtrb = ovtrf + 1;
5849           const PetscInt ovtlb = ovtrb + 1;
5850           PetscInt       o, n;
5851 
5852           /* Bottom Slice */
5853           /*   bottom */
5854           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
5855           for (o = oetf - 1; o >= oebf; --o)
5856             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5857           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
5858           /*   middle */
5859           for (i = 0; i < k - 1; ++i) {
5860             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
5861             for (n = 0; n < k - 1; ++n) {
5862               o = ofb + n * (k - 1) + i;
5863               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5864             }
5865             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
5866           }
5867           /*   top */
5868           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
5869           for (o = oebb; o < oebr; ++o)
5870             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5871           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
5872 
5873           /* Middle Slice */
5874           for (j = 0; j < k - 1; ++j) {
5875             /*   bottom */
5876             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
5877             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
5878               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5879             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
5880             /*   middle */
5881             for (i = 0; i < k - 1; ++i) {
5882               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
5883               for (n = 0; n < k - 1; ++n)
5884                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
5885               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
5886             }
5887             /*   top */
5888             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
5889             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
5890               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5891             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
5892           }
5893 
5894           /* Top Slice */
5895           /*   bottom */
5896           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
5897           for (o = oetf; o < oetr; ++o)
5898             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5899           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
5900           /*   middle */
5901           for (i = 0; i < k - 1; ++i) {
5902             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
5903             for (n = 0; n < k - 1; ++n)
5904               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
5905             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
5906           }
5907           /*   top */
5908           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
5909           for (o = oetl - 1; o >= oetb; --o)
5910             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5911           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
5912 
5913           foffset = offset;
5914         }
5915         break;
5916       default:
5917         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
5918       }
5919     }
5920     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
5921     /* Check permutation */
5922     {
5923       PetscInt *check;
5924 
5925       PetscCall(PetscMalloc1(size, &check));
5926       for (i = 0; i < size; ++i) {
5927         check[i] = -1;
5928         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
5929       }
5930       for (i = 0; i < size; ++i) check[perm[i]] = i;
5931       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
5932       PetscCall(PetscFree(check));
5933     }
5934     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
5935     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
5936       PetscInt *loc_perm;
5937       PetscCall(PetscMalloc1(size * 2, &loc_perm));
5938       for (PetscInt i = 0; i < size; i++) {
5939         loc_perm[i]        = perm[i];
5940         loc_perm[size + i] = size + perm[i];
5941       }
5942       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
5943     }
5944   }
5945   PetscFunctionReturn(PETSC_SUCCESS);
5946 }
5947 
5948 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
5949 {
5950   PetscDS  prob;
5951   PetscInt depth, Nf, h;
5952   DMLabel  label;
5953 
5954   PetscFunctionBeginHot;
5955   PetscCall(DMGetDS(dm, &prob));
5956   Nf      = prob->Nf;
5957   label   = dm->depthLabel;
5958   *dspace = NULL;
5959   if (field < Nf) {
5960     PetscObject disc = prob->disc[field];
5961 
5962     if (disc->classid == PETSCFE_CLASSID) {
5963       PetscDualSpace dsp;
5964 
5965       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
5966       PetscCall(DMLabelGetNumValues(label, &depth));
5967       PetscCall(DMLabelGetValue(label, point, &h));
5968       h = depth - 1 - h;
5969       if (h) {
5970         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
5971       } else {
5972         *dspace = dsp;
5973       }
5974     }
5975   }
5976   PetscFunctionReturn(PETSC_SUCCESS);
5977 }
5978 
5979 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5980 {
5981   PetscScalar       *array;
5982   const PetscScalar *vArray;
5983   const PetscInt    *cone, *coneO;
5984   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
5985 
5986   PetscFunctionBeginHot;
5987   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5988   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
5989   PetscCall(DMPlexGetCone(dm, point, &cone));
5990   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
5991   if (!values || !*values) {
5992     if ((point >= pStart) && (point < pEnd)) {
5993       PetscInt dof;
5994 
5995       PetscCall(PetscSectionGetDof(section, point, &dof));
5996       size += dof;
5997     }
5998     for (p = 0; p < numPoints; ++p) {
5999       const PetscInt cp = cone[p];
6000       PetscInt       dof;
6001 
6002       if ((cp < pStart) || (cp >= pEnd)) continue;
6003       PetscCall(PetscSectionGetDof(section, cp, &dof));
6004       size += dof;
6005     }
6006     if (!values) {
6007       if (csize) *csize = size;
6008       PetscFunctionReturn(PETSC_SUCCESS);
6009     }
6010     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
6011   } else {
6012     array = *values;
6013   }
6014   size = 0;
6015   PetscCall(VecGetArrayRead(v, &vArray));
6016   if ((point >= pStart) && (point < pEnd)) {
6017     PetscInt           dof, off, d;
6018     const PetscScalar *varr;
6019 
6020     PetscCall(PetscSectionGetDof(section, point, &dof));
6021     PetscCall(PetscSectionGetOffset(section, point, &off));
6022     varr = &vArray[off];
6023     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6024     size += dof;
6025   }
6026   for (p = 0; p < numPoints; ++p) {
6027     const PetscInt     cp = cone[p];
6028     PetscInt           o  = coneO[p];
6029     PetscInt           dof, off, d;
6030     const PetscScalar *varr;
6031 
6032     if ((cp < pStart) || (cp >= pEnd)) continue;
6033     PetscCall(PetscSectionGetDof(section, cp, &dof));
6034     PetscCall(PetscSectionGetOffset(section, cp, &off));
6035     varr = &vArray[off];
6036     if (o >= 0) {
6037       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6038     } else {
6039       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
6040     }
6041     size += dof;
6042   }
6043   PetscCall(VecRestoreArrayRead(v, &vArray));
6044   if (!*values) {
6045     if (csize) *csize = size;
6046     *values = array;
6047   } else {
6048     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6049     *csize = size;
6050   }
6051   PetscFunctionReturn(PETSC_SUCCESS);
6052 }
6053 
6054 /* Compress out points not in the section */
6055 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
6056 {
6057   const PetscInt np = *numPoints;
6058   PetscInt       pStart, pEnd, p, q;
6059 
6060   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6061   for (p = 0, q = 0; p < np; ++p) {
6062     const PetscInt r = points[p * 2];
6063     if ((r >= pStart) && (r < pEnd)) {
6064       points[q * 2]     = r;
6065       points[q * 2 + 1] = points[p * 2 + 1];
6066       ++q;
6067     }
6068   }
6069   *numPoints = q;
6070   return PETSC_SUCCESS;
6071 }
6072 
6073 /* Compressed closure does not apply closure permutation */
6074 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt ornt, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6075 {
6076   const PetscInt *cla = NULL;
6077   PetscInt        np, *pts = NULL;
6078 
6079   PetscFunctionBeginHot;
6080   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
6081   if (!ornt && *clPoints) {
6082     PetscInt dof, off;
6083 
6084     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
6085     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
6086     PetscCall(ISGetIndices(*clPoints, &cla));
6087     np  = dof / 2;
6088     pts = (PetscInt *)&cla[off];
6089   } else {
6090     PetscCall(DMPlexGetTransitiveClosure_Internal(dm, point, ornt, PETSC_TRUE, &np, &pts));
6091     PetscCall(CompressPoints_Private(section, &np, pts));
6092   }
6093   *numPoints = np;
6094   *points    = pts;
6095   *clp       = cla;
6096   PetscFunctionReturn(PETSC_SUCCESS);
6097 }
6098 
6099 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6100 {
6101   PetscFunctionBeginHot;
6102   if (!*clPoints) {
6103     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
6104   } else {
6105     PetscCall(ISRestoreIndices(*clPoints, clp));
6106   }
6107   *numPoints = 0;
6108   *points    = NULL;
6109   *clSec     = NULL;
6110   *clPoints  = NULL;
6111   *clp       = NULL;
6112   PetscFunctionReturn(PETSC_SUCCESS);
6113 }
6114 
6115 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
6116 {
6117   PetscInt            offset = 0, p;
6118   const PetscInt    **perms  = NULL;
6119   const PetscScalar **flips  = NULL;
6120 
6121   PetscFunctionBeginHot;
6122   *size = 0;
6123   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6124   for (p = 0; p < numPoints; p++) {
6125     const PetscInt     point = points[2 * p];
6126     const PetscInt    *perm  = perms ? perms[p] : NULL;
6127     const PetscScalar *flip  = flips ? flips[p] : NULL;
6128     PetscInt           dof, off, d;
6129     const PetscScalar *varr;
6130 
6131     PetscCall(PetscSectionGetDof(section, point, &dof));
6132     PetscCall(PetscSectionGetOffset(section, point, &off));
6133     varr = &vArray[off];
6134     if (clperm) {
6135       if (perm) {
6136         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
6137       } else {
6138         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
6139       }
6140       if (flip) {
6141         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
6142       }
6143     } else {
6144       if (perm) {
6145         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
6146       } else {
6147         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
6148       }
6149       if (flip) {
6150         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
6151       }
6152     }
6153     offset += dof;
6154   }
6155   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6156   *size = offset;
6157   PetscFunctionReturn(PETSC_SUCCESS);
6158 }
6159 
6160 static inline PetscErrorCode DMPlexVecGetClosure_Fields_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], PetscInt numFields, const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
6161 {
6162   PetscInt offset = 0, f;
6163 
6164   PetscFunctionBeginHot;
6165   *size = 0;
6166   for (f = 0; f < numFields; ++f) {
6167     PetscInt            p;
6168     const PetscInt    **perms = NULL;
6169     const PetscScalar **flips = NULL;
6170 
6171     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6172     for (p = 0; p < numPoints; p++) {
6173       const PetscInt     point = points[2 * p];
6174       PetscInt           fdof, foff, b;
6175       const PetscScalar *varr;
6176       const PetscInt    *perm = perms ? perms[p] : NULL;
6177       const PetscScalar *flip = flips ? flips[p] : NULL;
6178 
6179       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6180       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6181       varr = &vArray[foff];
6182       if (clperm) {
6183         if (perm) {
6184           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
6185         } else {
6186           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
6187         }
6188         if (flip) {
6189           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
6190         }
6191       } else {
6192         if (perm) {
6193           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
6194         } else {
6195           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
6196         }
6197         if (flip) {
6198           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
6199         }
6200       }
6201       offset += fdof;
6202     }
6203     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6204   }
6205   *size = offset;
6206   PetscFunctionReturn(PETSC_SUCCESS);
6207 }
6208 
6209 PetscErrorCode DMPlexVecGetOrientedClosure_Internal(DM dm, PetscSection section, PetscBool useClPerm, Vec v, PetscInt point, PetscInt ornt, PetscInt *csize, PetscScalar *values[])
6210 {
6211   PetscSection    clSection;
6212   IS              clPoints;
6213   PetscInt       *points = NULL;
6214   const PetscInt *clp, *perm = NULL;
6215   PetscInt        depth, numFields, numPoints, asize;
6216 
6217   PetscFunctionBeginHot;
6218   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6219   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6220   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6221   PetscValidHeaderSpecific(v, VEC_CLASSID, 4);
6222   PetscCall(DMPlexGetDepth(dm, &depth));
6223   PetscCall(PetscSectionGetNumFields(section, &numFields));
6224   if (depth == 1 && numFields < 2) {
6225     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6226     PetscFunctionReturn(PETSC_SUCCESS);
6227   }
6228   /* Get points */
6229   PetscCall(DMPlexGetCompressedClosure(dm, section, point, ornt, &numPoints, &points, &clSection, &clPoints, &clp));
6230   /* Get sizes */
6231   asize = 0;
6232   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6233     PetscInt dof;
6234     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6235     asize += dof;
6236   }
6237   if (values) {
6238     const PetscScalar *vArray;
6239     PetscInt           size;
6240 
6241     if (*values) {
6242       PetscCheck(*csize >= asize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Provided array size %" PetscInt_FMT " not sufficient to hold closure size %" PetscInt_FMT, *csize, asize);
6243     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6244     if (useClPerm) PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6245     PetscCall(VecGetArrayRead(v, &vArray));
6246     /* Get values */
6247     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6248     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6249     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6250     /* Cleanup array */
6251     PetscCall(VecRestoreArrayRead(v, &vArray));
6252   }
6253   if (csize) *csize = asize;
6254   /* Cleanup points */
6255   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6256   PetscFunctionReturn(PETSC_SUCCESS);
6257 }
6258 
6259 /*@C
6260   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
6261 
6262   Not collective
6263 
6264   Input Parameters:
6265 + dm      - The `DM`
6266 . section - The section describing the layout in `v`, or `NULL` to use the default section
6267 . v       - The local vector
6268 - point   - The point in the `DM`
6269 
6270   Input/Output Parameters:
6271 + csize  - The size of the input values array, or `NULL`; on output the number of values in the closure
6272 - values - An array to use for the values, or `NULL` to have it allocated automatically;
6273            if the user provided `NULL`, it is a borrowed array and should not be freed
6274 
6275   Level: intermediate
6276 
6277   Notes:
6278   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6279   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
6280   assembly function, and a user may already have allocated storage for this operation.
6281 
6282   A typical use could be
6283 .vb
6284    values = NULL;
6285    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6286    for (cl = 0; cl < clSize; ++cl) {
6287      <Compute on closure>
6288    }
6289    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
6290 .ve
6291   or
6292 .vb
6293    PetscMalloc1(clMaxSize, &values);
6294    for (p = pStart; p < pEnd; ++p) {
6295      clSize = clMaxSize;
6296      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6297      for (cl = 0; cl < clSize; ++cl) {
6298        <Compute on closure>
6299      }
6300    }
6301    PetscFree(values);
6302 .ve
6303 
6304   Fortran Notes:
6305   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6306 
6307 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6308 @*/
6309 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6310 {
6311   PetscFunctionBeginHot;
6312   PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, section, PETSC_TRUE, v, point, 0, csize, values));
6313   PetscFunctionReturn(PETSC_SUCCESS);
6314 }
6315 
6316 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6317 {
6318   DMLabel            depthLabel;
6319   PetscSection       clSection;
6320   IS                 clPoints;
6321   PetscScalar       *array;
6322   const PetscScalar *vArray;
6323   PetscInt          *points = NULL;
6324   const PetscInt    *clp, *perm = NULL;
6325   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6326 
6327   PetscFunctionBeginHot;
6328   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6329   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6330   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6331   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6332   PetscCall(DMPlexGetDepth(dm, &mdepth));
6333   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6334   PetscCall(PetscSectionGetNumFields(section, &numFields));
6335   if (mdepth == 1 && numFields < 2) {
6336     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6337     PetscFunctionReturn(PETSC_SUCCESS);
6338   }
6339   /* Get points */
6340   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6341   for (clsize = 0, p = 0; p < Np; p++) {
6342     PetscInt dof;
6343     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6344     clsize += dof;
6345   }
6346   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6347   /* Filter points */
6348   for (p = 0; p < numPoints * 2; p += 2) {
6349     PetscInt dep;
6350 
6351     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6352     if (dep != depth) continue;
6353     points[Np * 2 + 0] = points[p];
6354     points[Np * 2 + 1] = points[p + 1];
6355     ++Np;
6356   }
6357   /* Get array */
6358   if (!values || !*values) {
6359     PetscInt asize = 0, dof;
6360 
6361     for (p = 0; p < Np * 2; p += 2) {
6362       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6363       asize += dof;
6364     }
6365     if (!values) {
6366       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6367       if (csize) *csize = asize;
6368       PetscFunctionReturn(PETSC_SUCCESS);
6369     }
6370     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6371   } else {
6372     array = *values;
6373   }
6374   PetscCall(VecGetArrayRead(v, &vArray));
6375   /* Get values */
6376   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6377   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6378   /* Cleanup points */
6379   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6380   /* Cleanup array */
6381   PetscCall(VecRestoreArrayRead(v, &vArray));
6382   if (!*values) {
6383     if (csize) *csize = size;
6384     *values = array;
6385   } else {
6386     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6387     *csize = size;
6388   }
6389   PetscFunctionReturn(PETSC_SUCCESS);
6390 }
6391 
6392 /*@C
6393   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point'
6394 
6395   Not collective
6396 
6397   Input Parameters:
6398 + dm      - The `DM`
6399 . section - The section describing the layout in `v`, or `NULL` to use the default section
6400 . v       - The local vector
6401 . point   - The point in the `DM`
6402 . csize   - The number of values in the closure, or `NULL`
6403 - values  - The array of values, which is a borrowed array and should not be freed
6404 
6405   Level: intermediate
6406 
6407   Note:
6408   The array values are discarded and not copied back into `v`. In order to copy values back to `v`, use `DMPlexVecSetClosure()`
6409 
6410   Fortran Notes:
6411   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6412 
6413 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6414 @*/
6415 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6416 {
6417   PetscInt size = 0;
6418 
6419   PetscFunctionBegin;
6420   /* Should work without recalculating size */
6421   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6422   *values = NULL;
6423   PetscFunctionReturn(PETSC_SUCCESS);
6424 }
6425 
6426 static inline void add(PetscScalar *x, PetscScalar y)
6427 {
6428   *x += y;
6429 }
6430 static inline void insert(PetscScalar *x, PetscScalar y)
6431 {
6432   *x = y;
6433 }
6434 
6435 static inline PetscErrorCode updatePoint_private(PetscSection section, PetscInt point, PetscInt dof, void (*fuse)(PetscScalar *, PetscScalar), PetscBool setBC, const PetscInt perm[], const PetscScalar flip[], const PetscInt clperm[], const PetscScalar values[], PetscInt offset, PetscScalar array[])
6436 {
6437   PetscInt        cdof;  /* The number of constraints on this point */
6438   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6439   PetscScalar    *a;
6440   PetscInt        off, cind = 0, k;
6441 
6442   PetscFunctionBegin;
6443   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6444   PetscCall(PetscSectionGetOffset(section, point, &off));
6445   a = &array[off];
6446   if (!cdof || setBC) {
6447     if (clperm) {
6448       if (perm) {
6449         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6450       } else {
6451         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6452       }
6453     } else {
6454       if (perm) {
6455         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6456       } else {
6457         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6458       }
6459     }
6460   } else {
6461     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6462     if (clperm) {
6463       if (perm) {
6464         for (k = 0; k < dof; ++k) {
6465           if ((cind < cdof) && (k == cdofs[cind])) {
6466             ++cind;
6467             continue;
6468           }
6469           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6470         }
6471       } else {
6472         for (k = 0; k < dof; ++k) {
6473           if ((cind < cdof) && (k == cdofs[cind])) {
6474             ++cind;
6475             continue;
6476           }
6477           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6478         }
6479       }
6480     } else {
6481       if (perm) {
6482         for (k = 0; k < dof; ++k) {
6483           if ((cind < cdof) && (k == cdofs[cind])) {
6484             ++cind;
6485             continue;
6486           }
6487           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6488         }
6489       } else {
6490         for (k = 0; k < dof; ++k) {
6491           if ((cind < cdof) && (k == cdofs[cind])) {
6492             ++cind;
6493             continue;
6494           }
6495           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6496         }
6497       }
6498     }
6499   }
6500   PetscFunctionReturn(PETSC_SUCCESS);
6501 }
6502 
6503 static inline PetscErrorCode updatePointBC_private(PetscSection section, PetscInt point, PetscInt dof, void (*fuse)(PetscScalar *, PetscScalar), const PetscInt perm[], const PetscScalar flip[], const PetscInt clperm[], const PetscScalar values[], PetscInt offset, PetscScalar array[])
6504 {
6505   PetscInt        cdof;  /* The number of constraints on this point */
6506   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6507   PetscScalar    *a;
6508   PetscInt        off, cind = 0, k;
6509 
6510   PetscFunctionBegin;
6511   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6512   PetscCall(PetscSectionGetOffset(section, point, &off));
6513   a = &array[off];
6514   if (cdof) {
6515     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6516     if (clperm) {
6517       if (perm) {
6518         for (k = 0; k < dof; ++k) {
6519           if ((cind < cdof) && (k == cdofs[cind])) {
6520             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6521             cind++;
6522           }
6523         }
6524       } else {
6525         for (k = 0; k < dof; ++k) {
6526           if ((cind < cdof) && (k == cdofs[cind])) {
6527             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6528             cind++;
6529           }
6530         }
6531       }
6532     } else {
6533       if (perm) {
6534         for (k = 0; k < dof; ++k) {
6535           if ((cind < cdof) && (k == cdofs[cind])) {
6536             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6537             cind++;
6538           }
6539         }
6540       } else {
6541         for (k = 0; k < dof; ++k) {
6542           if ((cind < cdof) && (k == cdofs[cind])) {
6543             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6544             cind++;
6545           }
6546         }
6547       }
6548     }
6549   }
6550   PetscFunctionReturn(PETSC_SUCCESS);
6551 }
6552 
6553 static inline PetscErrorCode updatePointFields_private(PetscSection section, PetscInt point, const PetscInt *perm, const PetscScalar *flip, PetscInt f, void (*fuse)(PetscScalar *, PetscScalar), PetscBool setBC, const PetscInt clperm[], const PetscScalar values[], PetscInt *offset, PetscScalar array[])
6554 {
6555   PetscScalar    *a;
6556   PetscInt        fdof, foff, fcdof, foffset = *offset;
6557   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6558   PetscInt        cind = 0, b;
6559 
6560   PetscFunctionBegin;
6561   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6562   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6563   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6564   a = &array[foff];
6565   if (!fcdof || setBC) {
6566     if (clperm) {
6567       if (perm) {
6568         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6569       } else {
6570         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6571       }
6572     } else {
6573       if (perm) {
6574         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6575       } else {
6576         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6577       }
6578     }
6579   } else {
6580     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6581     if (clperm) {
6582       if (perm) {
6583         for (b = 0; b < fdof; b++) {
6584           if ((cind < fcdof) && (b == fcdofs[cind])) {
6585             ++cind;
6586             continue;
6587           }
6588           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6589         }
6590       } else {
6591         for (b = 0; b < fdof; b++) {
6592           if ((cind < fcdof) && (b == fcdofs[cind])) {
6593             ++cind;
6594             continue;
6595           }
6596           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6597         }
6598       }
6599     } else {
6600       if (perm) {
6601         for (b = 0; b < fdof; b++) {
6602           if ((cind < fcdof) && (b == fcdofs[cind])) {
6603             ++cind;
6604             continue;
6605           }
6606           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6607         }
6608       } else {
6609         for (b = 0; b < fdof; b++) {
6610           if ((cind < fcdof) && (b == fcdofs[cind])) {
6611             ++cind;
6612             continue;
6613           }
6614           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6615         }
6616       }
6617     }
6618   }
6619   *offset += fdof;
6620   PetscFunctionReturn(PETSC_SUCCESS);
6621 }
6622 
6623 static inline PetscErrorCode updatePointFieldsBC_private(PetscSection section, PetscInt point, const PetscInt perm[], const PetscScalar flip[], PetscInt f, PetscInt Ncc, const PetscInt comps[], void (*fuse)(PetscScalar *, PetscScalar), const PetscInt clperm[], const PetscScalar values[], PetscInt *offset, PetscScalar array[])
6624 {
6625   PetscScalar    *a;
6626   PetscInt        fdof, foff, fcdof, foffset = *offset;
6627   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6628   PetscInt        Nc, cind = 0, ncind = 0, b;
6629   PetscBool       ncSet, fcSet;
6630 
6631   PetscFunctionBegin;
6632   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6633   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6634   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6635   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6636   a = &array[foff];
6637   if (fcdof) {
6638     /* We just override fcdof and fcdofs with Ncc and comps */
6639     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6640     if (clperm) {
6641       if (perm) {
6642         if (comps) {
6643           for (b = 0; b < fdof; b++) {
6644             ncSet = fcSet = PETSC_FALSE;
6645             if (b % Nc == comps[ncind]) {
6646               ncind = (ncind + 1) % Ncc;
6647               ncSet = PETSC_TRUE;
6648             }
6649             if ((cind < fcdof) && (b == fcdofs[cind])) {
6650               ++cind;
6651               fcSet = PETSC_TRUE;
6652             }
6653             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6654           }
6655         } else {
6656           for (b = 0; b < fdof; b++) {
6657             if ((cind < fcdof) && (b == fcdofs[cind])) {
6658               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6659               ++cind;
6660             }
6661           }
6662         }
6663       } else {
6664         if (comps) {
6665           for (b = 0; b < fdof; b++) {
6666             ncSet = fcSet = PETSC_FALSE;
6667             if (b % Nc == comps[ncind]) {
6668               ncind = (ncind + 1) % Ncc;
6669               ncSet = PETSC_TRUE;
6670             }
6671             if ((cind < fcdof) && (b == fcdofs[cind])) {
6672               ++cind;
6673               fcSet = PETSC_TRUE;
6674             }
6675             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6676           }
6677         } else {
6678           for (b = 0; b < fdof; b++) {
6679             if ((cind < fcdof) && (b == fcdofs[cind])) {
6680               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6681               ++cind;
6682             }
6683           }
6684         }
6685       }
6686     } else {
6687       if (perm) {
6688         if (comps) {
6689           for (b = 0; b < fdof; b++) {
6690             ncSet = fcSet = PETSC_FALSE;
6691             if (b % Nc == comps[ncind]) {
6692               ncind = (ncind + 1) % Ncc;
6693               ncSet = PETSC_TRUE;
6694             }
6695             if ((cind < fcdof) && (b == fcdofs[cind])) {
6696               ++cind;
6697               fcSet = PETSC_TRUE;
6698             }
6699             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6700           }
6701         } else {
6702           for (b = 0; b < fdof; b++) {
6703             if ((cind < fcdof) && (b == fcdofs[cind])) {
6704               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6705               ++cind;
6706             }
6707           }
6708         }
6709       } else {
6710         if (comps) {
6711           for (b = 0; b < fdof; b++) {
6712             ncSet = fcSet = PETSC_FALSE;
6713             if (b % Nc == comps[ncind]) {
6714               ncind = (ncind + 1) % Ncc;
6715               ncSet = PETSC_TRUE;
6716             }
6717             if ((cind < fcdof) && (b == fcdofs[cind])) {
6718               ++cind;
6719               fcSet = PETSC_TRUE;
6720             }
6721             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6722           }
6723         } else {
6724           for (b = 0; b < fdof; b++) {
6725             if ((cind < fcdof) && (b == fcdofs[cind])) {
6726               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6727               ++cind;
6728             }
6729           }
6730         }
6731       }
6732     }
6733   }
6734   *offset += fdof;
6735   PetscFunctionReturn(PETSC_SUCCESS);
6736 }
6737 
6738 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6739 {
6740   PetscScalar    *array;
6741   const PetscInt *cone, *coneO;
6742   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6743 
6744   PetscFunctionBeginHot;
6745   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6746   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6747   PetscCall(DMPlexGetCone(dm, point, &cone));
6748   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6749   PetscCall(VecGetArray(v, &array));
6750   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6751     const PetscInt cp = !p ? point : cone[p - 1];
6752     const PetscInt o  = !p ? 0 : coneO[p - 1];
6753 
6754     if ((cp < pStart) || (cp >= pEnd)) {
6755       dof = 0;
6756       continue;
6757     }
6758     PetscCall(PetscSectionGetDof(section, cp, &dof));
6759     /* ADD_VALUES */
6760     {
6761       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6762       PetscScalar    *a;
6763       PetscInt        cdof, coff, cind = 0, k;
6764 
6765       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
6766       PetscCall(PetscSectionGetOffset(section, cp, &coff));
6767       a = &array[coff];
6768       if (!cdof) {
6769         if (o >= 0) {
6770           for (k = 0; k < dof; ++k) a[k] += values[off + k];
6771         } else {
6772           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
6773         }
6774       } else {
6775         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
6776         if (o >= 0) {
6777           for (k = 0; k < dof; ++k) {
6778             if ((cind < cdof) && (k == cdofs[cind])) {
6779               ++cind;
6780               continue;
6781             }
6782             a[k] += values[off + k];
6783           }
6784         } else {
6785           for (k = 0; k < dof; ++k) {
6786             if ((cind < cdof) && (k == cdofs[cind])) {
6787               ++cind;
6788               continue;
6789             }
6790             a[k] += values[off + dof - k - 1];
6791           }
6792         }
6793       }
6794     }
6795   }
6796   PetscCall(VecRestoreArray(v, &array));
6797   PetscFunctionReturn(PETSC_SUCCESS);
6798 }
6799 
6800 /*@C
6801   DMPlexVecSetClosure - Set an array of the values on the closure of `point`
6802 
6803   Not collective
6804 
6805   Input Parameters:
6806 + dm      - The `DM`
6807 . section - The section describing the layout in `v`, or `NULL` to use the default section
6808 . v       - The local vector
6809 . point   - The point in the `DM`
6810 . values  - The array of values
6811 - mode    - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
6812          where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
6813 
6814   Level: intermediate
6815 
6816 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
6817 @*/
6818 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6819 {
6820   PetscSection    clSection;
6821   IS              clPoints;
6822   PetscScalar    *array;
6823   PetscInt       *points = NULL;
6824   const PetscInt *clp, *clperm = NULL;
6825   PetscInt        depth, numFields, numPoints, p, clsize;
6826 
6827   PetscFunctionBeginHot;
6828   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6829   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6830   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6831   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6832   PetscCall(DMPlexGetDepth(dm, &depth));
6833   PetscCall(PetscSectionGetNumFields(section, &numFields));
6834   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
6835     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
6836     PetscFunctionReturn(PETSC_SUCCESS);
6837   }
6838   /* Get points */
6839   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6840   for (clsize = 0, p = 0; p < numPoints; p++) {
6841     PetscInt dof;
6842     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6843     clsize += dof;
6844   }
6845   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
6846   /* Get array */
6847   PetscCall(VecGetArray(v, &array));
6848   /* Get values */
6849   if (numFields > 0) {
6850     PetscInt offset = 0, f;
6851     for (f = 0; f < numFields; ++f) {
6852       const PetscInt    **perms = NULL;
6853       const PetscScalar **flips = NULL;
6854 
6855       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6856       switch (mode) {
6857       case INSERT_VALUES:
6858         for (p = 0; p < numPoints; p++) {
6859           const PetscInt     point = points[2 * p];
6860           const PetscInt    *perm  = perms ? perms[p] : NULL;
6861           const PetscScalar *flip  = flips ? flips[p] : NULL;
6862           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array));
6863         }
6864         break;
6865       case INSERT_ALL_VALUES:
6866         for (p = 0; p < numPoints; p++) {
6867           const PetscInt     point = points[2 * p];
6868           const PetscInt    *perm  = perms ? perms[p] : NULL;
6869           const PetscScalar *flip  = flips ? flips[p] : NULL;
6870           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array));
6871         }
6872         break;
6873       case INSERT_BC_VALUES:
6874         for (p = 0; p < numPoints; p++) {
6875           const PetscInt     point = points[2 * p];
6876           const PetscInt    *perm  = perms ? perms[p] : NULL;
6877           const PetscScalar *flip  = flips ? flips[p] : NULL;
6878           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array));
6879         }
6880         break;
6881       case ADD_VALUES:
6882         for (p = 0; p < numPoints; p++) {
6883           const PetscInt     point = points[2 * p];
6884           const PetscInt    *perm  = perms ? perms[p] : NULL;
6885           const PetscScalar *flip  = flips ? flips[p] : NULL;
6886           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array));
6887         }
6888         break;
6889       case ADD_ALL_VALUES:
6890         for (p = 0; p < numPoints; p++) {
6891           const PetscInt     point = points[2 * p];
6892           const PetscInt    *perm  = perms ? perms[p] : NULL;
6893           const PetscScalar *flip  = flips ? flips[p] : NULL;
6894           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array));
6895         }
6896         break;
6897       case ADD_BC_VALUES:
6898         for (p = 0; p < numPoints; p++) {
6899           const PetscInt     point = points[2 * p];
6900           const PetscInt    *perm  = perms ? perms[p] : NULL;
6901           const PetscScalar *flip  = flips ? flips[p] : NULL;
6902           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array));
6903         }
6904         break;
6905       default:
6906         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6907       }
6908       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6909     }
6910   } else {
6911     PetscInt            dof, off;
6912     const PetscInt    **perms = NULL;
6913     const PetscScalar **flips = NULL;
6914 
6915     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6916     switch (mode) {
6917     case INSERT_VALUES:
6918       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6919         const PetscInt     point = points[2 * p];
6920         const PetscInt    *perm  = perms ? perms[p] : NULL;
6921         const PetscScalar *flip  = flips ? flips[p] : NULL;
6922         PetscCall(PetscSectionGetDof(section, point, &dof));
6923         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array));
6924       }
6925       break;
6926     case INSERT_ALL_VALUES:
6927       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6928         const PetscInt     point = points[2 * p];
6929         const PetscInt    *perm  = perms ? perms[p] : NULL;
6930         const PetscScalar *flip  = flips ? flips[p] : NULL;
6931         PetscCall(PetscSectionGetDof(section, point, &dof));
6932         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array));
6933       }
6934       break;
6935     case INSERT_BC_VALUES:
6936       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6937         const PetscInt     point = points[2 * p];
6938         const PetscInt    *perm  = perms ? perms[p] : NULL;
6939         const PetscScalar *flip  = flips ? flips[p] : NULL;
6940         PetscCall(PetscSectionGetDof(section, point, &dof));
6941         PetscCall(updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array));
6942       }
6943       break;
6944     case ADD_VALUES:
6945       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6946         const PetscInt     point = points[2 * p];
6947         const PetscInt    *perm  = perms ? perms[p] : NULL;
6948         const PetscScalar *flip  = flips ? flips[p] : NULL;
6949         PetscCall(PetscSectionGetDof(section, point, &dof));
6950         PetscCall(updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array));
6951       }
6952       break;
6953     case ADD_ALL_VALUES:
6954       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6955         const PetscInt     point = points[2 * p];
6956         const PetscInt    *perm  = perms ? perms[p] : NULL;
6957         const PetscScalar *flip  = flips ? flips[p] : NULL;
6958         PetscCall(PetscSectionGetDof(section, point, &dof));
6959         PetscCall(updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array));
6960       }
6961       break;
6962     case ADD_BC_VALUES:
6963       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6964         const PetscInt     point = points[2 * p];
6965         const PetscInt    *perm  = perms ? perms[p] : NULL;
6966         const PetscScalar *flip  = flips ? flips[p] : NULL;
6967         PetscCall(PetscSectionGetDof(section, point, &dof));
6968         PetscCall(updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array));
6969       }
6970       break;
6971     default:
6972       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6973     }
6974     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6975   }
6976   /* Cleanup points */
6977   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6978   /* Cleanup array */
6979   PetscCall(VecRestoreArray(v, &array));
6980   PetscFunctionReturn(PETSC_SUCCESS);
6981 }
6982 
6983 /* Check whether the given point is in the label. If not, update the offset to skip this point */
6984 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
6985 {
6986   PetscFunctionBegin;
6987   *contains = PETSC_TRUE;
6988   if (label) {
6989     PetscInt fdof;
6990 
6991     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
6992     if (!*contains) {
6993       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6994       *offset += fdof;
6995       PetscFunctionReturn(PETSC_SUCCESS);
6996     }
6997   }
6998   PetscFunctionReturn(PETSC_SUCCESS);
6999 }
7000 
7001 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
7002 PetscErrorCode DMPlexVecSetFieldClosure_Internal(DM dm, PetscSection section, Vec v, PetscBool fieldActive[], PetscInt point, PetscInt Ncc, const PetscInt comps[], DMLabel label, PetscInt labelId, const PetscScalar values[], InsertMode mode)
7003 {
7004   PetscSection    clSection;
7005   IS              clPoints;
7006   PetscScalar    *array;
7007   PetscInt       *points = NULL;
7008   const PetscInt *clp;
7009   PetscInt        numFields, numPoints, p;
7010   PetscInt        offset = 0, f;
7011 
7012   PetscFunctionBeginHot;
7013   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7014   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7015   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7016   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7017   PetscCall(PetscSectionGetNumFields(section, &numFields));
7018   /* Get points */
7019   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7020   /* Get array */
7021   PetscCall(VecGetArray(v, &array));
7022   /* Get values */
7023   for (f = 0; f < numFields; ++f) {
7024     const PetscInt    **perms = NULL;
7025     const PetscScalar **flips = NULL;
7026     PetscBool           contains;
7027 
7028     if (!fieldActive[f]) {
7029       for (p = 0; p < numPoints * 2; p += 2) {
7030         PetscInt fdof;
7031         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7032         offset += fdof;
7033       }
7034       continue;
7035     }
7036     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7037     switch (mode) {
7038     case INSERT_VALUES:
7039       for (p = 0; p < numPoints; p++) {
7040         const PetscInt     point = points[2 * p];
7041         const PetscInt    *perm  = perms ? perms[p] : NULL;
7042         const PetscScalar *flip  = flips ? flips[p] : NULL;
7043         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7044         if (!contains) continue;
7045         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
7046       }
7047       break;
7048     case INSERT_ALL_VALUES:
7049       for (p = 0; p < numPoints; p++) {
7050         const PetscInt     point = points[2 * p];
7051         const PetscInt    *perm  = perms ? perms[p] : NULL;
7052         const PetscScalar *flip  = flips ? flips[p] : NULL;
7053         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7054         if (!contains) continue;
7055         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
7056       }
7057       break;
7058     case INSERT_BC_VALUES:
7059       for (p = 0; p < numPoints; p++) {
7060         const PetscInt     point = points[2 * p];
7061         const PetscInt    *perm  = perms ? perms[p] : NULL;
7062         const PetscScalar *flip  = flips ? flips[p] : NULL;
7063         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7064         if (!contains) continue;
7065         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
7066       }
7067       break;
7068     case ADD_VALUES:
7069       for (p = 0; p < numPoints; p++) {
7070         const PetscInt     point = points[2 * p];
7071         const PetscInt    *perm  = perms ? perms[p] : NULL;
7072         const PetscScalar *flip  = flips ? flips[p] : NULL;
7073         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7074         if (!contains) continue;
7075         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
7076       }
7077       break;
7078     case ADD_ALL_VALUES:
7079       for (p = 0; p < numPoints; p++) {
7080         const PetscInt     point = points[2 * p];
7081         const PetscInt    *perm  = perms ? perms[p] : NULL;
7082         const PetscScalar *flip  = flips ? flips[p] : NULL;
7083         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7084         if (!contains) continue;
7085         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
7086       }
7087       break;
7088     default:
7089       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7090     }
7091     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7092   }
7093   /* Cleanup points */
7094   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7095   /* Cleanup array */
7096   PetscCall(VecRestoreArray(v, &array));
7097   PetscFunctionReturn(PETSC_SUCCESS);
7098 }
7099 
7100 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
7101 {
7102   PetscMPIInt rank;
7103   PetscInt    i, j;
7104 
7105   PetscFunctionBegin;
7106   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7107   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
7108   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
7109   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
7110   numCIndices = numCIndices ? numCIndices : numRIndices;
7111   if (!values) PetscFunctionReturn(PETSC_SUCCESS);
7112   for (i = 0; i < numRIndices; i++) {
7113     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
7114     for (j = 0; j < numCIndices; j++) {
7115 #if defined(PETSC_USE_COMPLEX)
7116       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
7117 #else
7118       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
7119 #endif
7120     }
7121     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
7122   }
7123   PetscFunctionReturn(PETSC_SUCCESS);
7124 }
7125 
7126 /*
7127   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
7128 
7129   Input Parameters:
7130 + section - The section for this data layout
7131 . islocal - Is the section (and thus indices being requested) local or global?
7132 . point   - The point contributing dofs with these indices
7133 . off     - The global offset of this point
7134 . loff    - The local offset of each field
7135 . setBC   - The flag determining whether to include indices of boundary values
7136 . perm    - A permutation of the dofs on this point, or NULL
7137 - indperm - A permutation of the entire indices array, or NULL
7138 
7139   Output Parameter:
7140 . indices - Indices for dofs on this point
7141 
7142   Level: developer
7143 
7144   Note: The indices could be local or global, depending on the value of 'off'.
7145 */
7146 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
7147 {
7148   PetscInt        dof;   /* The number of unknowns on this point */
7149   PetscInt        cdof;  /* The number of constraints on this point */
7150   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7151   PetscInt        cind = 0, k;
7152 
7153   PetscFunctionBegin;
7154   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7155   PetscCall(PetscSectionGetDof(section, point, &dof));
7156   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
7157   if (!cdof || setBC) {
7158     for (k = 0; k < dof; ++k) {
7159       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7160       const PetscInt ind    = indperm ? indperm[preind] : preind;
7161 
7162       indices[ind] = off + k;
7163     }
7164   } else {
7165     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
7166     for (k = 0; k < dof; ++k) {
7167       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7168       const PetscInt ind    = indperm ? indperm[preind] : preind;
7169 
7170       if ((cind < cdof) && (k == cdofs[cind])) {
7171         /* Insert check for returning constrained indices */
7172         indices[ind] = -(off + k + 1);
7173         ++cind;
7174       } else {
7175         indices[ind] = off + k - (islocal ? 0 : cind);
7176       }
7177     }
7178   }
7179   *loff += dof;
7180   PetscFunctionReturn(PETSC_SUCCESS);
7181 }
7182 
7183 /*
7184  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
7185 
7186  Input Parameters:
7187 + section - a section (global or local)
7188 - islocal - `PETSC_TRUE` if requesting local indices (i.e., section is local); `PETSC_FALSE` for global
7189 . point - point within section
7190 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
7191 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
7192 . setBC - identify constrained (boundary condition) points via involution.
7193 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
7194 . permsoff - offset
7195 - indperm - index permutation
7196 
7197  Output Parameter:
7198 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
7199 . indices - array to hold indices (as defined by section) of each dof associated with point
7200 
7201  Notes:
7202  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
7203  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
7204  in the local vector.
7205 
7206  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
7207  significant).  It is invalid to call with a global section and setBC=true.
7208 
7209  Developer Note:
7210  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
7211  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
7212  offset could be obtained from the section instead of passing it explicitly as we do now.
7213 
7214  Example:
7215  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
7216  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
7217  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
7218  The global vector does not store constrained dofs, so when this function returns global indices, say {110, -112, 111}, the value of -112 is an arbitrary flag that should not be interpreted beyond its sign.
7219 
7220  Level: developer
7221 */
7222 PetscErrorCode DMPlexGetIndicesPointFields_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt foffs[], PetscBool setBC, const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7223 {
7224   PetscInt numFields, foff, f;
7225 
7226   PetscFunctionBegin;
7227   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7228   PetscCall(PetscSectionGetNumFields(section, &numFields));
7229   for (f = 0, foff = 0; f < numFields; ++f) {
7230     PetscInt        fdof, cfdof;
7231     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7232     PetscInt        cind = 0, b;
7233     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7234 
7235     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7236     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7237     if (!cfdof || setBC) {
7238       for (b = 0; b < fdof; ++b) {
7239         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7240         const PetscInt ind    = indperm ? indperm[preind] : preind;
7241 
7242         indices[ind] = off + foff + b;
7243       }
7244     } else {
7245       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7246       for (b = 0; b < fdof; ++b) {
7247         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7248         const PetscInt ind    = indperm ? indperm[preind] : preind;
7249 
7250         if ((cind < cfdof) && (b == fcdofs[cind])) {
7251           indices[ind] = -(off + foff + b + 1);
7252           ++cind;
7253         } else {
7254           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7255         }
7256       }
7257     }
7258     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7259     foffs[f] += fdof;
7260   }
7261   PetscFunctionReturn(PETSC_SUCCESS);
7262 }
7263 
7264 /*
7265   This version believes the globalSection offsets for each field, rather than just the point offset
7266 
7267  . foffs - The offset into 'indices' for each field, since it is segregated by field
7268 
7269  Notes:
7270  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7271  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7272 */
7273 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7274 {
7275   PetscInt numFields, foff, f;
7276 
7277   PetscFunctionBegin;
7278   PetscCall(PetscSectionGetNumFields(section, &numFields));
7279   for (f = 0; f < numFields; ++f) {
7280     PetscInt        fdof, cfdof;
7281     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7282     PetscInt        cind = 0, b;
7283     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7284 
7285     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7286     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7287     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7288     if (!cfdof) {
7289       for (b = 0; b < fdof; ++b) {
7290         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7291         const PetscInt ind    = indperm ? indperm[preind] : preind;
7292 
7293         indices[ind] = foff + b;
7294       }
7295     } else {
7296       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7297       for (b = 0; b < fdof; ++b) {
7298         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7299         const PetscInt ind    = indperm ? indperm[preind] : preind;
7300 
7301         if ((cind < cfdof) && (b == fcdofs[cind])) {
7302           indices[ind] = -(foff + b + 1);
7303           ++cind;
7304         } else {
7305           indices[ind] = foff + b - cind;
7306         }
7307       }
7308     }
7309     foffs[f] += fdof;
7310   }
7311   PetscFunctionReturn(PETSC_SUCCESS);
7312 }
7313 
7314 PetscErrorCode DMPlexAnchorsModifyMat(DM dm, PetscSection section, PetscInt numPoints, PetscInt numIndices, const PetscInt points[], const PetscInt ***perms, const PetscScalar values[], PetscInt *outNumPoints, PetscInt *outNumIndices, PetscInt *outPoints[], PetscScalar *outValues[], PetscInt offsets[], PetscBool multiplyLeft)
7315 {
7316   Mat             cMat;
7317   PetscSection    aSec, cSec;
7318   IS              aIS;
7319   PetscInt        aStart = -1, aEnd = -1;
7320   const PetscInt *anchors;
7321   PetscInt        numFields, f, p, q, newP = 0;
7322   PetscInt        newNumPoints = 0, newNumIndices = 0;
7323   PetscInt       *newPoints, *indices, *newIndices;
7324   PetscInt        maxAnchor, maxDof;
7325   PetscInt        newOffsets[32];
7326   PetscInt       *pointMatOffsets[32];
7327   PetscInt       *newPointOffsets[32];
7328   PetscScalar    *pointMat[32];
7329   PetscScalar    *newValues      = NULL, *tmpValues;
7330   PetscBool       anyConstrained = PETSC_FALSE;
7331 
7332   PetscFunctionBegin;
7333   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7334   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7335   PetscCall(PetscSectionGetNumFields(section, &numFields));
7336 
7337   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7338   /* if there are point-to-point constraints */
7339   if (aSec) {
7340     PetscCall(PetscArrayzero(newOffsets, 32));
7341     PetscCall(ISGetIndices(aIS, &anchors));
7342     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7343     /* figure out how many points are going to be in the new element matrix
7344      * (we allow double counting, because it's all just going to be summed
7345      * into the global matrix anyway) */
7346     for (p = 0; p < 2 * numPoints; p += 2) {
7347       PetscInt b    = points[p];
7348       PetscInt bDof = 0, bSecDof;
7349 
7350       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7351       if (!bSecDof) continue;
7352       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7353       if (bDof) {
7354         /* this point is constrained */
7355         /* it is going to be replaced by its anchors */
7356         PetscInt bOff, q;
7357 
7358         anyConstrained = PETSC_TRUE;
7359         newNumPoints += bDof;
7360         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7361         for (q = 0; q < bDof; q++) {
7362           PetscInt a = anchors[bOff + q];
7363           PetscInt aDof;
7364 
7365           PetscCall(PetscSectionGetDof(section, a, &aDof));
7366           newNumIndices += aDof;
7367           for (f = 0; f < numFields; ++f) {
7368             PetscInt fDof;
7369 
7370             PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7371             newOffsets[f + 1] += fDof;
7372           }
7373         }
7374       } else {
7375         /* this point is not constrained */
7376         newNumPoints++;
7377         newNumIndices += bSecDof;
7378         for (f = 0; f < numFields; ++f) {
7379           PetscInt fDof;
7380 
7381           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7382           newOffsets[f + 1] += fDof;
7383         }
7384       }
7385     }
7386   }
7387   if (!anyConstrained) {
7388     if (outNumPoints) *outNumPoints = 0;
7389     if (outNumIndices) *outNumIndices = 0;
7390     if (outPoints) *outPoints = NULL;
7391     if (outValues) *outValues = NULL;
7392     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7393     PetscFunctionReturn(PETSC_SUCCESS);
7394   }
7395 
7396   if (outNumPoints) *outNumPoints = newNumPoints;
7397   if (outNumIndices) *outNumIndices = newNumIndices;
7398 
7399   for (f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7400 
7401   if (!outPoints && !outValues) {
7402     if (offsets) {
7403       for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7404     }
7405     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7406     PetscFunctionReturn(PETSC_SUCCESS);
7407   }
7408 
7409   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7410 
7411   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7412 
7413   /* workspaces */
7414   if (numFields) {
7415     for (f = 0; f < numFields; f++) {
7416       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7417       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7418     }
7419   } else {
7420     PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7421     PetscCall(DMGetWorkArray(dm, numPoints, MPIU_INT, &newPointOffsets[0]));
7422   }
7423 
7424   /* get workspaces for the point-to-point matrices */
7425   if (numFields) {
7426     PetscInt totalOffset, totalMatOffset;
7427 
7428     for (p = 0; p < numPoints; p++) {
7429       PetscInt b    = points[2 * p];
7430       PetscInt bDof = 0, bSecDof;
7431 
7432       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7433       if (!bSecDof) {
7434         for (f = 0; f < numFields; f++) {
7435           newPointOffsets[f][p + 1] = 0;
7436           pointMatOffsets[f][p + 1] = 0;
7437         }
7438         continue;
7439       }
7440       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7441       if (bDof) {
7442         for (f = 0; f < numFields; f++) {
7443           PetscInt fDof, q, bOff, allFDof = 0;
7444 
7445           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7446           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7447           for (q = 0; q < bDof; q++) {
7448             PetscInt a = anchors[bOff + q];
7449             PetscInt aFDof;
7450 
7451             PetscCall(PetscSectionGetFieldDof(section, a, f, &aFDof));
7452             allFDof += aFDof;
7453           }
7454           newPointOffsets[f][p + 1] = allFDof;
7455           pointMatOffsets[f][p + 1] = fDof * allFDof;
7456         }
7457       } else {
7458         for (f = 0; f < numFields; f++) {
7459           PetscInt fDof;
7460 
7461           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7462           newPointOffsets[f][p + 1] = fDof;
7463           pointMatOffsets[f][p + 1] = 0;
7464         }
7465       }
7466     }
7467     for (f = 0, totalOffset = 0, totalMatOffset = 0; f < numFields; f++) {
7468       newPointOffsets[f][0] = totalOffset;
7469       pointMatOffsets[f][0] = totalMatOffset;
7470       for (p = 0; p < numPoints; p++) {
7471         newPointOffsets[f][p + 1] += newPointOffsets[f][p];
7472         pointMatOffsets[f][p + 1] += pointMatOffsets[f][p];
7473       }
7474       totalOffset    = newPointOffsets[f][numPoints];
7475       totalMatOffset = pointMatOffsets[f][numPoints];
7476       PetscCall(DMGetWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7477     }
7478   } else {
7479     for (p = 0; p < numPoints; p++) {
7480       PetscInt b    = points[2 * p];
7481       PetscInt bDof = 0, bSecDof;
7482 
7483       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7484       if (!bSecDof) {
7485         newPointOffsets[0][p + 1] = 0;
7486         pointMatOffsets[0][p + 1] = 0;
7487         continue;
7488       }
7489       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7490       if (bDof) {
7491         PetscInt bOff, q, allDof = 0;
7492 
7493         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7494         for (q = 0; q < bDof; q++) {
7495           PetscInt a = anchors[bOff + q], aDof;
7496 
7497           PetscCall(PetscSectionGetDof(section, a, &aDof));
7498           allDof += aDof;
7499         }
7500         newPointOffsets[0][p + 1] = allDof;
7501         pointMatOffsets[0][p + 1] = bSecDof * allDof;
7502       } else {
7503         newPointOffsets[0][p + 1] = bSecDof;
7504         pointMatOffsets[0][p + 1] = 0;
7505       }
7506     }
7507     newPointOffsets[0][0] = 0;
7508     pointMatOffsets[0][0] = 0;
7509     for (p = 0; p < numPoints; p++) {
7510       newPointOffsets[0][p + 1] += newPointOffsets[0][p];
7511       pointMatOffsets[0][p + 1] += pointMatOffsets[0][p];
7512     }
7513     PetscCall(DMGetWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7514   }
7515 
7516   /* output arrays */
7517   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7518 
7519   /* get the point-to-point matrices; construct newPoints */
7520   PetscCall(PetscSectionGetMaxDof(aSec, &maxAnchor));
7521   PetscCall(PetscSectionGetMaxDof(section, &maxDof));
7522   PetscCall(DMGetWorkArray(dm, maxDof, MPIU_INT, &indices));
7523   PetscCall(DMGetWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7524   if (numFields) {
7525     for (p = 0, newP = 0; p < numPoints; p++) {
7526       PetscInt b    = points[2 * p];
7527       PetscInt o    = points[2 * p + 1];
7528       PetscInt bDof = 0, bSecDof;
7529 
7530       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7531       if (!bSecDof) continue;
7532       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7533       if (bDof) {
7534         PetscInt fStart[32], fEnd[32], fAnchorStart[32], fAnchorEnd[32], bOff, q;
7535 
7536         fStart[0] = 0;
7537         fEnd[0]   = 0;
7538         for (f = 0; f < numFields; f++) {
7539           PetscInt fDof;
7540 
7541           PetscCall(PetscSectionGetFieldDof(cSec, b, f, &fDof));
7542           fStart[f + 1] = fStart[f] + fDof;
7543           fEnd[f + 1]   = fStart[f + 1];
7544         }
7545         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7546         PetscCall(DMPlexGetIndicesPointFields_Internal(cSec, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, p, NULL, indices));
7547 
7548         fAnchorStart[0] = 0;
7549         fAnchorEnd[0]   = 0;
7550         for (f = 0; f < numFields; f++) {
7551           PetscInt fDof = newPointOffsets[f][p + 1] - newPointOffsets[f][p];
7552 
7553           fAnchorStart[f + 1] = fAnchorStart[f] + fDof;
7554           fAnchorEnd[f + 1]   = fAnchorStart[f + 1];
7555         }
7556         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7557         for (q = 0; q < bDof; q++) {
7558           PetscInt a = anchors[bOff + q], aOff;
7559 
7560           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7561           newPoints[2 * (newP + q)]     = a;
7562           newPoints[2 * (newP + q) + 1] = 0;
7563           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7564           PetscCall(DMPlexGetIndicesPointFields_Internal(section, PETSC_TRUE, a, aOff, fAnchorEnd, PETSC_TRUE, NULL, -1, NULL, newIndices));
7565         }
7566         newP += bDof;
7567 
7568         if (outValues) {
7569           /* get the point-to-point submatrix */
7570           for (f = 0; f < numFields; f++) PetscCall(MatGetValues(cMat, fEnd[f] - fStart[f], indices + fStart[f], fAnchorEnd[f] - fAnchorStart[f], newIndices + fAnchorStart[f], pointMat[f] + pointMatOffsets[f][p]));
7571         }
7572       } else {
7573         newPoints[2 * newP]     = b;
7574         newPoints[2 * newP + 1] = o;
7575         newP++;
7576       }
7577     }
7578   } else {
7579     for (p = 0; p < numPoints; p++) {
7580       PetscInt b    = points[2 * p];
7581       PetscInt o    = points[2 * p + 1];
7582       PetscInt bDof = 0, bSecDof;
7583 
7584       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7585       if (!bSecDof) continue;
7586       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7587       if (bDof) {
7588         PetscInt bEnd = 0, bAnchorEnd = 0, bOff;
7589 
7590         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7591         PetscCall(DMPlexGetIndicesPoint_Internal(cSec, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, indices));
7592 
7593         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7594         for (q = 0; q < bDof; q++) {
7595           PetscInt a = anchors[bOff + q], aOff;
7596 
7597           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7598 
7599           newPoints[2 * (newP + q)]     = a;
7600           newPoints[2 * (newP + q) + 1] = 0;
7601           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7602           PetscCall(DMPlexGetIndicesPoint_Internal(section, PETSC_TRUE, a, aOff, &bAnchorEnd, PETSC_TRUE, NULL, NULL, newIndices));
7603         }
7604         newP += bDof;
7605 
7606         /* get the point-to-point submatrix */
7607         if (outValues) PetscCall(MatGetValues(cMat, bEnd, indices, bAnchorEnd, newIndices, pointMat[0] + pointMatOffsets[0][p]));
7608       } else {
7609         newPoints[2 * newP]     = b;
7610         newPoints[2 * newP + 1] = o;
7611         newP++;
7612       }
7613     }
7614   }
7615 
7616   if (outValues) {
7617     PetscCall(DMGetWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7618     PetscCall(PetscArrayzero(tmpValues, newNumIndices * numIndices));
7619     /* multiply constraints on the right */
7620     if (numFields) {
7621       for (f = 0; f < numFields; f++) {
7622         PetscInt oldOff = offsets[f];
7623 
7624         for (p = 0; p < numPoints; p++) {
7625           PetscInt cStart = newPointOffsets[f][p];
7626           PetscInt b      = points[2 * p];
7627           PetscInt c, r, k;
7628           PetscInt dof;
7629 
7630           PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7631           if (!dof) continue;
7632           if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7633             PetscInt           nCols = newPointOffsets[f][p + 1] - cStart;
7634             const PetscScalar *mat   = pointMat[f] + pointMatOffsets[f][p];
7635 
7636             for (r = 0; r < numIndices; r++) {
7637               for (c = 0; c < nCols; c++) {
7638                 for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += values[r * numIndices + oldOff + k] * mat[k * nCols + c];
7639               }
7640             }
7641           } else {
7642             /* copy this column as is */
7643             for (r = 0; r < numIndices; r++) {
7644               for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7645             }
7646           }
7647           oldOff += dof;
7648         }
7649       }
7650     } else {
7651       PetscInt oldOff = 0;
7652       for (p = 0; p < numPoints; p++) {
7653         PetscInt cStart = newPointOffsets[0][p];
7654         PetscInt b      = points[2 * p];
7655         PetscInt c, r, k;
7656         PetscInt dof;
7657 
7658         PetscCall(PetscSectionGetDof(section, b, &dof));
7659         if (!dof) continue;
7660         if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7661           PetscInt           nCols = newPointOffsets[0][p + 1] - cStart;
7662           const PetscScalar *mat   = pointMat[0] + pointMatOffsets[0][p];
7663 
7664           for (r = 0; r < numIndices; r++) {
7665             for (c = 0; c < nCols; c++) {
7666               for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += mat[k * nCols + c] * values[r * numIndices + oldOff + k];
7667             }
7668           }
7669         } else {
7670           /* copy this column as is */
7671           for (r = 0; r < numIndices; r++) {
7672             for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7673           }
7674         }
7675         oldOff += dof;
7676       }
7677     }
7678 
7679     if (multiplyLeft) {
7680       PetscCall(DMGetWorkArray(dm, newNumIndices * newNumIndices, MPIU_SCALAR, &newValues));
7681       PetscCall(PetscArrayzero(newValues, newNumIndices * newNumIndices));
7682       /* multiply constraints transpose on the left */
7683       if (numFields) {
7684         for (f = 0; f < numFields; f++) {
7685           PetscInt oldOff = offsets[f];
7686 
7687           for (p = 0; p < numPoints; p++) {
7688             PetscInt rStart = newPointOffsets[f][p];
7689             PetscInt b      = points[2 * p];
7690             PetscInt c, r, k;
7691             PetscInt dof;
7692 
7693             PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7694             if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7695               PetscInt                          nRows = newPointOffsets[f][p + 1] - rStart;
7696               const PetscScalar *PETSC_RESTRICT mat   = pointMat[f] + pointMatOffsets[f][p];
7697 
7698               for (r = 0; r < nRows; r++) {
7699                 for (c = 0; c < newNumIndices; c++) {
7700                   for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7701                 }
7702               }
7703             } else {
7704               /* copy this row as is */
7705               for (r = 0; r < dof; r++) {
7706                 for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7707               }
7708             }
7709             oldOff += dof;
7710           }
7711         }
7712       } else {
7713         PetscInt oldOff = 0;
7714 
7715         for (p = 0; p < numPoints; p++) {
7716           PetscInt rStart = newPointOffsets[0][p];
7717           PetscInt b      = points[2 * p];
7718           PetscInt c, r, k;
7719           PetscInt dof;
7720 
7721           PetscCall(PetscSectionGetDof(section, b, &dof));
7722           if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7723             PetscInt                          nRows = newPointOffsets[0][p + 1] - rStart;
7724             const PetscScalar *PETSC_RESTRICT mat   = pointMat[0] + pointMatOffsets[0][p];
7725 
7726             for (r = 0; r < nRows; r++) {
7727               for (c = 0; c < newNumIndices; c++) {
7728                 for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7729               }
7730             }
7731           } else {
7732             /* copy this row as is */
7733             for (r = 0; r < dof; r++) {
7734               for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7735             }
7736           }
7737           oldOff += dof;
7738         }
7739       }
7740 
7741       PetscCall(DMRestoreWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7742     } else {
7743       newValues = tmpValues;
7744     }
7745   }
7746 
7747   /* clean up */
7748   PetscCall(DMRestoreWorkArray(dm, maxDof, MPIU_INT, &indices));
7749   PetscCall(DMRestoreWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7750 
7751   if (numFields) {
7752     for (f = 0; f < numFields; f++) {
7753       PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7754       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7755       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7756     }
7757   } else {
7758     PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7759     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7760     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[0]));
7761   }
7762   PetscCall(ISRestoreIndices(aIS, &anchors));
7763 
7764   /* output */
7765   if (outPoints) {
7766     *outPoints = newPoints;
7767   } else {
7768     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7769   }
7770   if (outValues) *outValues = newValues;
7771   for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7772   PetscFunctionReturn(PETSC_SUCCESS);
7773 }
7774 
7775 /*@C
7776   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
7777 
7778   Not collective
7779 
7780   Input Parameters:
7781 + dm         - The `DM`
7782 . section    - The `PetscSection` describing the points (a local section)
7783 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
7784 . point      - The point defining the closure
7785 - useClPerm  - Use the closure point permutation if available
7786 
7787   Output Parameters:
7788 + numIndices - The number of dof indices in the closure of point with the input sections
7789 . indices    - The dof indices
7790 . outOffsets - Array to write the field offsets into, or `NULL`
7791 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
7792 
7793   Level: advanced
7794 
7795   Notes:
7796   Must call `DMPlexRestoreClosureIndices()` to free allocated memory
7797 
7798   If `idxSection` is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
7799   of those indices is not significant.  If `idxSection` is local, the constrained dofs will yield the involution -(idx+1)
7800   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7801   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when `idxSection` == section, otherwise global
7802   indices (with the above semantics) are implied.
7803 
7804 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
7805           `PetscSection`, `DMGetGlobalSection()`
7806 @*/
7807 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7808 {
7809   /* Closure ordering */
7810   PetscSection    clSection;
7811   IS              clPoints;
7812   const PetscInt *clp;
7813   PetscInt       *points;
7814   const PetscInt *clperm = NULL;
7815   /* Dof permutation and sign flips */
7816   const PetscInt    **perms[32] = {NULL};
7817   const PetscScalar **flips[32] = {NULL};
7818   PetscScalar        *valCopy   = NULL;
7819   /* Hanging node constraints */
7820   PetscInt    *pointsC = NULL;
7821   PetscScalar *valuesC = NULL;
7822   PetscInt     NclC, NiC;
7823 
7824   PetscInt *idx;
7825   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7826   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7827 
7828   PetscFunctionBeginHot;
7829   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7830   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7831   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7832   if (numIndices) PetscAssertPointer(numIndices, 6);
7833   if (indices) PetscAssertPointer(indices, 7);
7834   if (outOffsets) PetscAssertPointer(outOffsets, 8);
7835   if (values) PetscAssertPointer(values, 9);
7836   PetscCall(PetscSectionGetNumFields(section, &Nf));
7837   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7838   PetscCall(PetscArrayzero(offsets, 32));
7839   /* 1) Get points in closure */
7840   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7841   if (useClPerm) {
7842     PetscInt depth, clsize;
7843     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7844     for (clsize = 0, p = 0; p < Ncl; p++) {
7845       PetscInt dof;
7846       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7847       clsize += dof;
7848     }
7849     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7850   }
7851   /* 2) Get number of indices on these points and field offsets from section */
7852   for (p = 0; p < Ncl * 2; p += 2) {
7853     PetscInt dof, fdof;
7854 
7855     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7856     for (f = 0; f < Nf; ++f) {
7857       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7858       offsets[f + 1] += fdof;
7859     }
7860     Ni += dof;
7861   }
7862   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
7863   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7864   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7865   for (f = 0; f < PetscMax(1, Nf); ++f) {
7866     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7867     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
7868     /* may need to apply sign changes to the element matrix */
7869     if (values && flips[f]) {
7870       PetscInt foffset = offsets[f];
7871 
7872       for (p = 0; p < Ncl; ++p) {
7873         PetscInt           pnt  = points[2 * p], fdof;
7874         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
7875 
7876         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
7877         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
7878         if (flip) {
7879           PetscInt i, j, k;
7880 
7881           if (!valCopy) {
7882             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7883             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
7884             *values = valCopy;
7885           }
7886           for (i = 0; i < fdof; ++i) {
7887             PetscScalar fval = flip[i];
7888 
7889             for (k = 0; k < Ni; ++k) {
7890               valCopy[Ni * (foffset + i) + k] *= fval;
7891               valCopy[Ni * k + (foffset + i)] *= fval;
7892             }
7893           }
7894         }
7895         foffset += fdof;
7896       }
7897     }
7898   }
7899   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
7900   PetscCall(DMPlexAnchorsModifyMat(dm, section, Ncl, Ni, points, perms, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, PETSC_TRUE));
7901   if (NclC) {
7902     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7903     for (f = 0; f < PetscMax(1, Nf); ++f) {
7904       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7905       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7906     }
7907     for (f = 0; f < PetscMax(1, Nf); ++f) {
7908       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
7909       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
7910     }
7911     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7912     Ncl    = NclC;
7913     Ni     = NiC;
7914     points = pointsC;
7915     if (values) *values = valuesC;
7916   }
7917   /* 5) Calculate indices */
7918   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
7919   if (Nf) {
7920     PetscInt  idxOff;
7921     PetscBool useFieldOffsets;
7922 
7923     if (outOffsets) {
7924       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
7925     }
7926     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
7927     if (useFieldOffsets) {
7928       for (p = 0; p < Ncl; ++p) {
7929         const PetscInt pnt = points[p * 2];
7930 
7931         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
7932       }
7933     } else {
7934       for (p = 0; p < Ncl; ++p) {
7935         const PetscInt pnt = points[p * 2];
7936 
7937         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7938         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7939          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
7940          * global section. */
7941         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
7942       }
7943     }
7944   } else {
7945     PetscInt off = 0, idxOff;
7946 
7947     for (p = 0; p < Ncl; ++p) {
7948       const PetscInt  pnt  = points[p * 2];
7949       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
7950 
7951       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7952       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7953        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
7954       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
7955     }
7956   }
7957   /* 6) Cleanup */
7958   for (f = 0; f < PetscMax(1, Nf); ++f) {
7959     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7960     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7961   }
7962   if (NclC) {
7963     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
7964   } else {
7965     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7966   }
7967 
7968   if (numIndices) *numIndices = Ni;
7969   if (indices) *indices = idx;
7970   PetscFunctionReturn(PETSC_SUCCESS);
7971 }
7972 
7973 /*@C
7974   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
7975 
7976   Not collective
7977 
7978   Input Parameters:
7979 + dm         - The `DM`
7980 . section    - The `PetscSection` describing the points (a local section)
7981 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
7982 . point      - The point defining the closure
7983 - useClPerm  - Use the closure point permutation if available
7984 
7985   Output Parameters:
7986 + numIndices - The number of dof indices in the closure of point with the input sections
7987 . indices    - The dof indices
7988 . outOffsets - Array to write the field offsets into, or `NULL`
7989 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
7990 
7991   Level: advanced
7992 
7993   Notes:
7994   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
7995 
7996   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
7997   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7998   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7999   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
8000   indices (with the above semantics) are implied.
8001 
8002 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
8003 @*/
8004 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
8005 {
8006   PetscFunctionBegin;
8007   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8008   PetscAssertPointer(indices, 7);
8009   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
8010   PetscFunctionReturn(PETSC_SUCCESS);
8011 }
8012 
8013 PetscErrorCode DMPlexMatSetClosure_Internal(DM dm, PetscSection section, PetscSection globalSection, PetscBool useClPerm, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8014 {
8015   DM_Plex           *mesh = (DM_Plex *)dm->data;
8016   PetscInt          *indices;
8017   PetscInt           numIndices;
8018   const PetscScalar *valuesOrig = values;
8019   PetscErrorCode     ierr;
8020 
8021   PetscFunctionBegin;
8022   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8023   if (!section) PetscCall(DMGetLocalSection(dm, &section));
8024   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
8025   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
8026   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
8027   PetscValidHeaderSpecific(A, MAT_CLASSID, 5);
8028 
8029   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, useClPerm, &numIndices, &indices, NULL, (PetscScalar **)&values));
8030 
8031   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
8032   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8033   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
8034   if (ierr) {
8035     PetscMPIInt rank;
8036 
8037     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8038     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8039     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
8040     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8041     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8042     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
8043   }
8044   if (mesh->printFEM > 1) {
8045     PetscInt i;
8046     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
8047     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
8048     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8049   }
8050 
8051   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8052   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8053   PetscFunctionReturn(PETSC_SUCCESS);
8054 }
8055 
8056 /*@C
8057   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
8058 
8059   Not collective
8060 
8061   Input Parameters:
8062 + dm            - The `DM`
8063 . section       - The section describing the layout in `v`, or `NULL` to use the default section
8064 . globalSection - The section describing the layout in `v`, or `NULL` to use the default global section
8065 . A             - The matrix
8066 . point         - The point in the `DM`
8067 . values        - The array of values
8068 - mode          - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8069 
8070   Level: intermediate
8071 
8072 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8073 @*/
8074 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8075 {
8076   PetscFunctionBegin;
8077   PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, PETSC_TRUE, A, point, values, mode));
8078   PetscFunctionReturn(PETSC_SUCCESS);
8079 }
8080 
8081 /*@C
8082   DMPlexMatSetClosureGeneral - Set an array of the values on the closure of 'point' using a different row and column section
8083 
8084   Not collective
8085 
8086   Input Parameters:
8087 + dmRow            - The `DM` for the row fields
8088 . sectionRow       - The section describing the layout, or `NULL` to use the default section in `dmRow`
8089 . useRowPerm       - The flag to use the closure permutation of the `dmRow` if available
8090 . globalSectionRow - The section describing the layout, or `NULL` to use the default global section in `dmRow`
8091 . dmCol            - The `DM` for the column fields
8092 . sectionCol       - The section describing the layout, or `NULL` to use the default section in `dmCol`
8093 . useColPerm       - The flag to use the closure permutation of the `dmCol` if available
8094 . globalSectionCol - The section describing the layout, or `NULL` to use the default global section in `dmCol`
8095 . A                - The matrix
8096 . point            - The point in the `DM`
8097 . values           - The array of values
8098 - mode             - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8099 
8100   Level: intermediate
8101 
8102 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8103 @*/
8104 PetscErrorCode DMPlexMatSetClosureGeneral(DM dmRow, PetscSection sectionRow, PetscSection globalSectionRow, PetscBool useRowPerm, DM dmCol, PetscSection sectionCol, PetscSection globalSectionCol, PetscBool useColPerm, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8105 {
8106   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
8107   PetscInt          *indicesRow, *indicesCol;
8108   PetscInt           numIndicesRow, numIndicesCol;
8109   const PetscScalar *valuesOrig = values;
8110   PetscErrorCode     ierr;
8111 
8112   PetscFunctionBegin;
8113   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
8114   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
8115   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
8116   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
8117   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
8118   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 5);
8119   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
8120   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 6);
8121   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
8122   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 7);
8123   PetscValidHeaderSpecific(A, MAT_CLASSID, 9);
8124 
8125   PetscCall(DMPlexGetClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
8126   PetscCall(DMPlexGetClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
8127 
8128   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
8129   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8130   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values, mode);
8131   if (ierr) {
8132     PetscMPIInt rank;
8133 
8134     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8135     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8136     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
8137     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
8138     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&values));
8139     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
8140   }
8141 
8142   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
8143   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
8144   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
8145   PetscFunctionReturn(PETSC_SUCCESS);
8146 }
8147 
8148 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8149 {
8150   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
8151   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8152   PetscInt       *cpoints = NULL;
8153   PetscInt       *findices, *cindices;
8154   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8155   PetscInt        foffsets[32], coffsets[32];
8156   DMPolytopeType  ct;
8157   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8158   PetscErrorCode  ierr;
8159 
8160   PetscFunctionBegin;
8161   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8162   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8163   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8164   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8165   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8166   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8167   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8168   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8169   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8170   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8171   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
8172   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8173   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8174   PetscCall(PetscArrayzero(foffsets, 32));
8175   PetscCall(PetscArrayzero(coffsets, 32));
8176   /* Column indices */
8177   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8178   maxFPoints = numCPoints;
8179   /* Compress out points not in the section */
8180   /*   TODO: Squeeze out points with 0 dof as well */
8181   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8182   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8183     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8184       cpoints[q * 2]     = cpoints[p];
8185       cpoints[q * 2 + 1] = cpoints[p + 1];
8186       ++q;
8187     }
8188   }
8189   numCPoints = q;
8190   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8191     PetscInt fdof;
8192 
8193     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8194     if (!dof) continue;
8195     for (f = 0; f < numFields; ++f) {
8196       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8197       coffsets[f + 1] += fdof;
8198     }
8199     numCIndices += dof;
8200   }
8201   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8202   /* Row indices */
8203   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8204   {
8205     DMPlexTransform tr;
8206     DMPolytopeType *rct;
8207     PetscInt       *rsize, *rcone, *rornt, Nt;
8208 
8209     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8210     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8211     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8212     numSubcells = rsize[Nt - 1];
8213     PetscCall(DMPlexTransformDestroy(&tr));
8214   }
8215   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8216   for (r = 0, q = 0; r < numSubcells; ++r) {
8217     /* TODO Map from coarse to fine cells */
8218     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8219     /* Compress out points not in the section */
8220     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8221     for (p = 0; p < numFPoints * 2; p += 2) {
8222       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8223         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8224         if (!dof) continue;
8225         for (s = 0; s < q; ++s)
8226           if (fpoints[p] == ftotpoints[s * 2]) break;
8227         if (s < q) continue;
8228         ftotpoints[q * 2]     = fpoints[p];
8229         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8230         ++q;
8231       }
8232     }
8233     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8234   }
8235   numFPoints = q;
8236   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8237     PetscInt fdof;
8238 
8239     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8240     if (!dof) continue;
8241     for (f = 0; f < numFields; ++f) {
8242       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8243       foffsets[f + 1] += fdof;
8244     }
8245     numFIndices += dof;
8246   }
8247   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8248 
8249   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8250   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8251   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8252   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8253   if (numFields) {
8254     const PetscInt **permsF[32] = {NULL};
8255     const PetscInt **permsC[32] = {NULL};
8256 
8257     for (f = 0; f < numFields; f++) {
8258       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8259       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8260     }
8261     for (p = 0; p < numFPoints; p++) {
8262       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8263       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8264     }
8265     for (p = 0; p < numCPoints; p++) {
8266       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8267       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8268     }
8269     for (f = 0; f < numFields; f++) {
8270       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8271       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8272     }
8273   } else {
8274     const PetscInt **permsF = NULL;
8275     const PetscInt **permsC = NULL;
8276 
8277     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8278     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8279     for (p = 0, off = 0; p < numFPoints; p++) {
8280       const PetscInt *perm = permsF ? permsF[p] : NULL;
8281 
8282       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8283       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8284     }
8285     for (p = 0, off = 0; p < numCPoints; p++) {
8286       const PetscInt *perm = permsC ? permsC[p] : NULL;
8287 
8288       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8289       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8290     }
8291     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8292     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8293   }
8294   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8295   /* TODO: flips */
8296   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8297   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8298   if (ierr) {
8299     PetscMPIInt rank;
8300 
8301     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8302     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8303     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8304     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8305     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8306   }
8307   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8308   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8309   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8310   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8311   PetscFunctionReturn(PETSC_SUCCESS);
8312 }
8313 
8314 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8315 {
8316   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8317   PetscInt       *cpoints = NULL;
8318   PetscInt        foffsets[32], coffsets[32];
8319   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8320   DMPolytopeType  ct;
8321   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8322 
8323   PetscFunctionBegin;
8324   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8325   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8326   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8327   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8328   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8329   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8330   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8331   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8332   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8333   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8334   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8335   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8336   PetscCall(PetscArrayzero(foffsets, 32));
8337   PetscCall(PetscArrayzero(coffsets, 32));
8338   /* Column indices */
8339   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8340   maxFPoints = numCPoints;
8341   /* Compress out points not in the section */
8342   /*   TODO: Squeeze out points with 0 dof as well */
8343   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8344   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8345     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8346       cpoints[q * 2]     = cpoints[p];
8347       cpoints[q * 2 + 1] = cpoints[p + 1];
8348       ++q;
8349     }
8350   }
8351   numCPoints = q;
8352   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8353     PetscInt fdof;
8354 
8355     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8356     if (!dof) continue;
8357     for (f = 0; f < numFields; ++f) {
8358       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8359       coffsets[f + 1] += fdof;
8360     }
8361     numCIndices += dof;
8362   }
8363   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8364   /* Row indices */
8365   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8366   {
8367     DMPlexTransform tr;
8368     DMPolytopeType *rct;
8369     PetscInt       *rsize, *rcone, *rornt, Nt;
8370 
8371     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8372     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8373     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8374     numSubcells = rsize[Nt - 1];
8375     PetscCall(DMPlexTransformDestroy(&tr));
8376   }
8377   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8378   for (r = 0, q = 0; r < numSubcells; ++r) {
8379     /* TODO Map from coarse to fine cells */
8380     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8381     /* Compress out points not in the section */
8382     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8383     for (p = 0; p < numFPoints * 2; p += 2) {
8384       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8385         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8386         if (!dof) continue;
8387         for (s = 0; s < q; ++s)
8388           if (fpoints[p] == ftotpoints[s * 2]) break;
8389         if (s < q) continue;
8390         ftotpoints[q * 2]     = fpoints[p];
8391         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8392         ++q;
8393       }
8394     }
8395     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8396   }
8397   numFPoints = q;
8398   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8399     PetscInt fdof;
8400 
8401     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8402     if (!dof) continue;
8403     for (f = 0; f < numFields; ++f) {
8404       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8405       foffsets[f + 1] += fdof;
8406     }
8407     numFIndices += dof;
8408   }
8409   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8410 
8411   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8412   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8413   if (numFields) {
8414     const PetscInt **permsF[32] = {NULL};
8415     const PetscInt **permsC[32] = {NULL};
8416 
8417     for (f = 0; f < numFields; f++) {
8418       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8419       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8420     }
8421     for (p = 0; p < numFPoints; p++) {
8422       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8423       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8424     }
8425     for (p = 0; p < numCPoints; p++) {
8426       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8427       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8428     }
8429     for (f = 0; f < numFields; f++) {
8430       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8431       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8432     }
8433   } else {
8434     const PetscInt **permsF = NULL;
8435     const PetscInt **permsC = NULL;
8436 
8437     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8438     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8439     for (p = 0, off = 0; p < numFPoints; p++) {
8440       const PetscInt *perm = permsF ? permsF[p] : NULL;
8441 
8442       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8443       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8444     }
8445     for (p = 0, off = 0; p < numCPoints; p++) {
8446       const PetscInt *perm = permsC ? permsC[p] : NULL;
8447 
8448       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8449       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8450     }
8451     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8452     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8453   }
8454   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8455   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8456   PetscFunctionReturn(PETSC_SUCCESS);
8457 }
8458 
8459 /*@C
8460   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8461 
8462   Input Parameter:
8463 . dm - The `DMPLEX` object
8464 
8465   Output Parameter:
8466 . cellHeight - The height of a cell
8467 
8468   Level: developer
8469 
8470 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetVTKCellHeight()`
8471 @*/
8472 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8473 {
8474   DM_Plex *mesh = (DM_Plex *)dm->data;
8475 
8476   PetscFunctionBegin;
8477   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8478   PetscAssertPointer(cellHeight, 2);
8479   *cellHeight = mesh->vtkCellHeight;
8480   PetscFunctionReturn(PETSC_SUCCESS);
8481 }
8482 
8483 /*@C
8484   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8485 
8486   Input Parameters:
8487 + dm         - The `DMPLEX` object
8488 - cellHeight - The height of a cell
8489 
8490   Level: developer
8491 
8492 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8493 @*/
8494 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8495 {
8496   DM_Plex *mesh = (DM_Plex *)dm->data;
8497 
8498   PetscFunctionBegin;
8499   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8500   mesh->vtkCellHeight = cellHeight;
8501   PetscFunctionReturn(PETSC_SUCCESS);
8502 }
8503 
8504 /*@
8505   DMPlexGetCellTypeStratum - Get the range of cells of a given celltype
8506 
8507   Input Parameters:
8508 + dm - The `DMPLEX` object
8509 - ct - The `DMPolytopeType` of the cell
8510 
8511   Output Parameters:
8512 + start - The first cell of this type, or `NULL`
8513 - end   - The upper bound on this celltype, or `NULL`
8514 
8515   Level: advanced
8516 
8517 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8518 @*/
8519 PetscErrorCode DMPlexGetCellTypeStratum(DM dm, DMPolytopeType ct, PetscInt *start, PetscInt *end)
8520 {
8521   DM_Plex *mesh = (DM_Plex *)dm->data;
8522   DMLabel  label;
8523   PetscInt pStart, pEnd;
8524 
8525   PetscFunctionBegin;
8526   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8527   if (start) {
8528     PetscAssertPointer(start, 3);
8529     *start = 0;
8530   }
8531   if (end) {
8532     PetscAssertPointer(end, 4);
8533     *end = 0;
8534   }
8535   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8536   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
8537   if (mesh->tr) {
8538     PetscCall(DMPlexTransformGetCellTypeStratum(mesh->tr, ct, start, end));
8539   } else {
8540     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
8541     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named celltype was found");
8542     PetscCall(DMLabelGetStratumBounds(label, ct, start, end));
8543   }
8544   PetscFunctionReturn(PETSC_SUCCESS);
8545 }
8546 
8547 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8548 {
8549   PetscSection section, globalSection;
8550   PetscInt    *numbers, p;
8551 
8552   PetscFunctionBegin;
8553   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8554   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8555   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8556   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8557   PetscCall(PetscSectionSetUp(section));
8558   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_FALSE, &globalSection));
8559   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8560   for (p = pStart; p < pEnd; ++p) {
8561     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8562     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8563     else numbers[p - pStart] += shift;
8564   }
8565   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8566   if (globalSize) {
8567     PetscLayout layout;
8568     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8569     PetscCall(PetscLayoutGetSize(layout, globalSize));
8570     PetscCall(PetscLayoutDestroy(&layout));
8571   }
8572   PetscCall(PetscSectionDestroy(&section));
8573   PetscCall(PetscSectionDestroy(&globalSection));
8574   PetscFunctionReturn(PETSC_SUCCESS);
8575 }
8576 
8577 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
8578 {
8579   PetscInt cellHeight, cStart, cEnd;
8580 
8581   PetscFunctionBegin;
8582   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8583   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8584   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8585   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8586   PetscFunctionReturn(PETSC_SUCCESS);
8587 }
8588 
8589 /*@
8590   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8591 
8592   Input Parameter:
8593 . dm - The `DMPLEX` object
8594 
8595   Output Parameter:
8596 . globalCellNumbers - Global cell numbers for all cells on this process
8597 
8598   Level: developer
8599 
8600 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVertexNumbering()`
8601 @*/
8602 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8603 {
8604   DM_Plex *mesh = (DM_Plex *)dm->data;
8605 
8606   PetscFunctionBegin;
8607   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8608   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8609   *globalCellNumbers = mesh->globalCellNumbers;
8610   PetscFunctionReturn(PETSC_SUCCESS);
8611 }
8612 
8613 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8614 {
8615   PetscInt vStart, vEnd;
8616 
8617   PetscFunctionBegin;
8618   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8619   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8620   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8621   PetscFunctionReturn(PETSC_SUCCESS);
8622 }
8623 
8624 /*@
8625   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8626 
8627   Input Parameter:
8628 . dm - The `DMPLEX` object
8629 
8630   Output Parameter:
8631 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8632 
8633   Level: developer
8634 
8635 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8636 @*/
8637 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8638 {
8639   DM_Plex *mesh = (DM_Plex *)dm->data;
8640 
8641   PetscFunctionBegin;
8642   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8643   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8644   *globalVertexNumbers = mesh->globalVertexNumbers;
8645   PetscFunctionReturn(PETSC_SUCCESS);
8646 }
8647 
8648 /*@
8649   DMPlexCreatePointNumbering - Create a global numbering for all points.
8650 
8651   Collective
8652 
8653   Input Parameter:
8654 . dm - The `DMPLEX` object
8655 
8656   Output Parameter:
8657 . globalPointNumbers - Global numbers for all points on this process
8658 
8659   Level: developer
8660 
8661   Notes:
8662   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8663   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8664   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8665   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8666 
8667   The partitioned mesh is
8668   ```
8669   (2)--0--(3)--1--(4)    (1)--0--(2)
8670   ```
8671   and its global numbering is
8672   ```
8673   (3)--0--(4)--1--(5)--2--(6)
8674   ```
8675   Then the global numbering is provided as
8676   ```
8677   [0] Number of indices in set 5
8678   [0] 0 0
8679   [0] 1 1
8680   [0] 2 3
8681   [0] 3 4
8682   [0] 4 -6
8683   [1] Number of indices in set 3
8684   [1] 0 2
8685   [1] 1 5
8686   [1] 2 6
8687   ```
8688 
8689 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8690 @*/
8691 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8692 {
8693   IS        nums[4];
8694   PetscInt  depths[4], gdepths[4], starts[4];
8695   PetscInt  depth, d, shift = 0;
8696   PetscBool empty = PETSC_FALSE;
8697 
8698   PetscFunctionBegin;
8699   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8700   PetscCall(DMPlexGetDepth(dm, &depth));
8701   // For unstratified meshes use dim instead of depth
8702   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8703   // If any stratum is empty, we must mark all empty
8704   for (d = 0; d <= depth; ++d) {
8705     PetscInt end;
8706 
8707     depths[d] = depth - d;
8708     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8709     if (!(starts[d] - end)) empty = PETSC_TRUE;
8710   }
8711   if (empty)
8712     for (d = 0; d <= depth; ++d) {
8713       depths[d] = -1;
8714       starts[d] = -1;
8715     }
8716   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8717   PetscCall(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8718   for (d = 0; d <= depth; ++d) PetscCheck(starts[d] < 0 || depths[d] == gdepths[d], PETSC_COMM_SELF, PETSC_ERR_PLIB, "Expected depth %" PetscInt_FMT ", found %" PetscInt_FMT, depths[d], gdepths[d]);
8719   // Note here that 'shift' is collective, so that the numbering is stratified by depth
8720   for (d = 0; d <= depth; ++d) {
8721     PetscInt pStart, pEnd, gsize;
8722 
8723     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8724     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8725     shift += gsize;
8726   }
8727   PetscCall(ISConcatenate(PETSC_COMM_SELF, depth + 1, nums, globalPointNumbers));
8728   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8729   PetscFunctionReturn(PETSC_SUCCESS);
8730 }
8731 
8732 /*@
8733   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8734 
8735   Input Parameter:
8736 . dm - The `DMPLEX` object
8737 
8738   Output Parameter:
8739 . ranks - The rank field
8740 
8741   Options Database Key:
8742 . -dm_partition_view - Adds the rank field into the `DM` output from `-dm_view` using the same viewer
8743 
8744   Level: intermediate
8745 
8746 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8747 @*/
8748 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8749 {
8750   DM             rdm;
8751   PetscFE        fe;
8752   PetscScalar   *r;
8753   PetscMPIInt    rank;
8754   DMPolytopeType ct;
8755   PetscInt       dim, cStart, cEnd, c;
8756   PetscBool      simplex;
8757 
8758   PetscFunctionBeginUser;
8759   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8760   PetscAssertPointer(ranks, 2);
8761   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
8762   PetscCall(DMClone(dm, &rdm));
8763   PetscCall(DMGetDimension(rdm, &dim));
8764   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8765   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8766   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
8767   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8768   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
8769   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8770   PetscCall(PetscFEDestroy(&fe));
8771   PetscCall(DMCreateDS(rdm));
8772   PetscCall(DMCreateGlobalVector(rdm, ranks));
8773   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
8774   PetscCall(VecGetArray(*ranks, &r));
8775   for (c = cStart; c < cEnd; ++c) {
8776     PetscScalar *lr;
8777 
8778     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8779     if (lr) *lr = rank;
8780   }
8781   PetscCall(VecRestoreArray(*ranks, &r));
8782   PetscCall(DMDestroy(&rdm));
8783   PetscFunctionReturn(PETSC_SUCCESS);
8784 }
8785 
8786 /*@
8787   DMPlexCreateLabelField - Create a cell field whose value is the label value for that cell
8788 
8789   Input Parameters:
8790 + dm    - The `DMPLEX`
8791 - label - The `DMLabel`
8792 
8793   Output Parameter:
8794 . val - The label value field
8795 
8796   Options Database Key:
8797 . -dm_label_view - Adds the label value field into the `DM` output from `-dm_view` using the same viewer
8798 
8799   Level: intermediate
8800 
8801 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8802 @*/
8803 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
8804 {
8805   DM           rdm;
8806   PetscFE      fe;
8807   PetscScalar *v;
8808   PetscInt     dim, cStart, cEnd, c;
8809 
8810   PetscFunctionBeginUser;
8811   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8812   PetscAssertPointer(label, 2);
8813   PetscAssertPointer(val, 3);
8814   PetscCall(DMClone(dm, &rdm));
8815   PetscCall(DMGetDimension(rdm, &dim));
8816   PetscCall(PetscFECreateDefault(PetscObjectComm((PetscObject)rdm), dim, 1, PETSC_TRUE, "PETSc___label_value_", -1, &fe));
8817   PetscCall(PetscObjectSetName((PetscObject)fe, "label_value"));
8818   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8819   PetscCall(PetscFEDestroy(&fe));
8820   PetscCall(DMCreateDS(rdm));
8821   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8822   PetscCall(DMCreateGlobalVector(rdm, val));
8823   PetscCall(PetscObjectSetName((PetscObject)*val, "label_value"));
8824   PetscCall(VecGetArray(*val, &v));
8825   for (c = cStart; c < cEnd; ++c) {
8826     PetscScalar *lv;
8827     PetscInt     cval;
8828 
8829     PetscCall(DMPlexPointGlobalRef(rdm, c, v, &lv));
8830     PetscCall(DMLabelGetValue(label, c, &cval));
8831     *lv = cval;
8832   }
8833   PetscCall(VecRestoreArray(*val, &v));
8834   PetscCall(DMDestroy(&rdm));
8835   PetscFunctionReturn(PETSC_SUCCESS);
8836 }
8837 
8838 /*@
8839   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
8840 
8841   Input Parameter:
8842 . dm - The `DMPLEX` object
8843 
8844   Level: developer
8845 
8846   Notes:
8847   This is a useful diagnostic when creating meshes programmatically.
8848 
8849   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8850 
8851 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8852 @*/
8853 PetscErrorCode DMPlexCheckSymmetry(DM dm)
8854 {
8855   PetscSection    coneSection, supportSection;
8856   const PetscInt *cone, *support;
8857   PetscInt        coneSize, c, supportSize, s;
8858   PetscInt        pStart, pEnd, p, pp, csize, ssize;
8859   PetscBool       storagecheck = PETSC_TRUE;
8860 
8861   PetscFunctionBegin;
8862   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8863   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
8864   PetscCall(DMPlexGetConeSection(dm, &coneSection));
8865   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
8866   /* Check that point p is found in the support of its cone points, and vice versa */
8867   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8868   for (p = pStart; p < pEnd; ++p) {
8869     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
8870     PetscCall(DMPlexGetCone(dm, p, &cone));
8871     for (c = 0; c < coneSize; ++c) {
8872       PetscBool dup = PETSC_FALSE;
8873       PetscInt  d;
8874       for (d = c - 1; d >= 0; --d) {
8875         if (cone[c] == cone[d]) {
8876           dup = PETSC_TRUE;
8877           break;
8878         }
8879       }
8880       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
8881       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
8882       for (s = 0; s < supportSize; ++s) {
8883         if (support[s] == p) break;
8884       }
8885       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
8886         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
8887         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
8888         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8889         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
8890         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
8891         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8892         PetscCheck(!dup, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not repeatedly found in support of repeated cone point %" PetscInt_FMT, p, cone[c]);
8893         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
8894       }
8895     }
8896     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
8897     if (p != pp) {
8898       storagecheck = PETSC_FALSE;
8899       continue;
8900     }
8901     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
8902     PetscCall(DMPlexGetSupport(dm, p, &support));
8903     for (s = 0; s < supportSize; ++s) {
8904       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
8905       PetscCall(DMPlexGetCone(dm, support[s], &cone));
8906       for (c = 0; c < coneSize; ++c) {
8907         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
8908         if (cone[c] != pp) {
8909           c = 0;
8910           break;
8911         }
8912         if (cone[c] == p) break;
8913       }
8914       if (c >= coneSize) {
8915         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
8916         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
8917         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8918         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
8919         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
8920         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8921         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
8922       }
8923     }
8924   }
8925   if (storagecheck) {
8926     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
8927     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
8928     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
8929   }
8930   PetscFunctionReturn(PETSC_SUCCESS);
8931 }
8932 
8933 /*
8934   For submeshes with cohesive cells (see DMPlexConstructCohesiveCells()), we allow a special case where some of the boundary of a face (edges and vertices) are not duplicated. We call these special boundary points "unsplit", since the same edge or vertex appears in both copies of the face. These unsplit points throw off our counting, so we have to explicitly account for them here.
8935 */
8936 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
8937 {
8938   DMPolytopeType  cct;
8939   PetscInt        ptpoints[4];
8940   const PetscInt *cone, *ccone, *ptcone;
8941   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
8942 
8943   PetscFunctionBegin;
8944   *unsplit = 0;
8945   switch (ct) {
8946   case DM_POLYTOPE_POINT_PRISM_TENSOR:
8947     ptpoints[npt++] = c;
8948     break;
8949   case DM_POLYTOPE_SEG_PRISM_TENSOR:
8950     PetscCall(DMPlexGetCone(dm, c, &cone));
8951     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8952     for (cp = 0; cp < coneSize; ++cp) {
8953       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
8954       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
8955     }
8956     break;
8957   case DM_POLYTOPE_TRI_PRISM_TENSOR:
8958   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8959     PetscCall(DMPlexGetCone(dm, c, &cone));
8960     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8961     for (cp = 0; cp < coneSize; ++cp) {
8962       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
8963       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
8964       for (ccp = 0; ccp < cconeSize; ++ccp) {
8965         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
8966         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
8967           PetscInt p;
8968           for (p = 0; p < npt; ++p)
8969             if (ptpoints[p] == ccone[ccp]) break;
8970           if (p == npt) ptpoints[npt++] = ccone[ccp];
8971         }
8972       }
8973     }
8974     break;
8975   default:
8976     break;
8977   }
8978   for (pt = 0; pt < npt; ++pt) {
8979     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
8980     if (ptcone[0] == ptcone[1]) ++(*unsplit);
8981   }
8982   PetscFunctionReturn(PETSC_SUCCESS);
8983 }
8984 
8985 /*@
8986   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
8987 
8988   Input Parameters:
8989 + dm         - The `DMPLEX` object
8990 - cellHeight - Normally 0
8991 
8992   Level: developer
8993 
8994   Notes:
8995   This is a useful diagnostic when creating meshes programmatically.
8996   Currently applicable only to homogeneous simplex or tensor meshes.
8997 
8998   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8999 
9000 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9001 @*/
9002 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
9003 {
9004   DMPlexInterpolatedFlag interp;
9005   DMPolytopeType         ct;
9006   PetscInt               vStart, vEnd, cStart, cEnd, c;
9007 
9008   PetscFunctionBegin;
9009   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9010   PetscCall(DMPlexIsInterpolated(dm, &interp));
9011   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9012   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9013   for (c = cStart; c < cEnd; ++c) {
9014     PetscInt *closure = NULL;
9015     PetscInt  coneSize, closureSize, cl, Nv = 0;
9016 
9017     PetscCall(DMPlexGetCellType(dm, c, &ct));
9018     PetscCheck((PetscInt)ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
9019     if (ct == DM_POLYTOPE_UNKNOWN) continue;
9020     if (interp == DMPLEX_INTERPOLATED_FULL) {
9021       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9022       PetscCheck(coneSize == DMPolytopeTypeGetConeSize(ct), PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " of type %s has cone size %" PetscInt_FMT " != %" PetscInt_FMT, c, DMPolytopeTypes[ct], coneSize, DMPolytopeTypeGetConeSize(ct));
9023     }
9024     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9025     for (cl = 0; cl < closureSize * 2; cl += 2) {
9026       const PetscInt p = closure[cl];
9027       if ((p >= vStart) && (p < vEnd)) ++Nv;
9028     }
9029     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9030     /* Special Case: Tensor faces with identified vertices */
9031     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
9032       PetscInt unsplit;
9033 
9034       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9035       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
9036     }
9037     PetscCheck(Nv == DMPolytopeTypeGetNumVertices(ct), PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " of type %s has %" PetscInt_FMT " vertices != %" PetscInt_FMT, c, DMPolytopeTypes[ct], Nv, DMPolytopeTypeGetNumVertices(ct));
9038   }
9039   PetscFunctionReturn(PETSC_SUCCESS);
9040 }
9041 
9042 /*@
9043   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
9044 
9045   Collective
9046 
9047   Input Parameters:
9048 + dm         - The `DMPLEX` object
9049 - cellHeight - Normally 0
9050 
9051   Level: developer
9052 
9053   Notes:
9054   This is a useful diagnostic when creating meshes programmatically.
9055   This routine is only relevant for meshes that are fully interpolated across all ranks.
9056   It will error out if a partially interpolated mesh is given on some rank.
9057   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
9058 
9059   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9060 
9061 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
9062 @*/
9063 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
9064 {
9065   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
9066   DMPlexInterpolatedFlag interpEnum;
9067 
9068   PetscFunctionBegin;
9069   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9070   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
9071   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(PETSC_SUCCESS);
9072   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
9073     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported"));
9074     PetscFunctionReturn(PETSC_SUCCESS);
9075   }
9076 
9077   PetscCall(DMGetDimension(dm, &dim));
9078   PetscCall(DMPlexGetDepth(dm, &depth));
9079   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9080   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
9081     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
9082     for (c = cStart; c < cEnd; ++c) {
9083       const PetscInt       *cone, *ornt, *faceSizes, *faces;
9084       const DMPolytopeType *faceTypes;
9085       DMPolytopeType        ct;
9086       PetscInt              numFaces, coneSize, f;
9087       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
9088 
9089       PetscCall(DMPlexGetCellType(dm, c, &ct));
9090       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9091       if (unsplit) continue;
9092       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9093       PetscCall(DMPlexGetCone(dm, c, &cone));
9094       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
9095       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9096       for (cl = 0; cl < closureSize * 2; cl += 2) {
9097         const PetscInt p = closure[cl];
9098         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
9099       }
9100       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9101       PetscCheck(coneSize == numFaces, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " of type %s has %" PetscInt_FMT " faces but should have %" PetscInt_FMT, c, DMPolytopeTypes[ct], coneSize, numFaces);
9102       for (f = 0; f < numFaces; ++f) {
9103         DMPolytopeType fct;
9104         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
9105 
9106         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
9107         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
9108         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
9109           const PetscInt p = fclosure[cl];
9110           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
9111         }
9112         PetscCheck(fnumCorners == faceSizes[f], PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " of type %s (cone idx %" PetscInt_FMT ") of cell %" PetscInt_FMT " of type %s has %" PetscInt_FMT " vertices but should have %" PetscInt_FMT, cone[f], DMPolytopeTypes[fct], f, c, DMPolytopeTypes[ct], fnumCorners, faceSizes[f]);
9113         for (v = 0; v < fnumCorners; ++v) {
9114           if (fclosure[v] != faces[fOff + v]) {
9115             PetscInt v1;
9116 
9117             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
9118             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
9119             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
9120             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
9121             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9122             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " of type %s (cone idx %" PetscInt_FMT ", ornt %" PetscInt_FMT ") of cell %" PetscInt_FMT " of type %s vertex %" PetscInt_FMT ", %" PetscInt_FMT " != %" PetscInt_FMT, cone[f], DMPolytopeTypes[fct], f, ornt[f], c, DMPolytopeTypes[ct], v, fclosure[v], faces[fOff + v]);
9123           }
9124         }
9125         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
9126         fOff += faceSizes[f];
9127       }
9128       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9129       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9130     }
9131   }
9132   PetscFunctionReturn(PETSC_SUCCESS);
9133 }
9134 
9135 /*@
9136   DMPlexCheckGeometry - Check the geometry of mesh cells
9137 
9138   Input Parameter:
9139 . dm - The `DMPLEX` object
9140 
9141   Level: developer
9142 
9143   Notes:
9144   This is a useful diagnostic when creating meshes programmatically.
9145 
9146   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9147 
9148 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9149 @*/
9150 PetscErrorCode DMPlexCheckGeometry(DM dm)
9151 {
9152   Vec       coordinates;
9153   PetscReal detJ, J[9], refVol = 1.0;
9154   PetscReal vol;
9155   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
9156 
9157   PetscFunctionBegin;
9158   PetscCall(DMGetDimension(dm, &dim));
9159   PetscCall(DMGetCoordinateDim(dm, &dE));
9160   if (dim != dE) PetscFunctionReturn(PETSC_SUCCESS);
9161   PetscCall(DMPlexGetDepth(dm, &depth));
9162   for (d = 0; d < dim; ++d) refVol *= 2.0;
9163   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9164   /* Make sure local coordinates are created, because that step is collective */
9165   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
9166   if (!coordinates) PetscFunctionReturn(PETSC_SUCCESS);
9167   for (c = cStart; c < cEnd; ++c) {
9168     DMPolytopeType ct;
9169     PetscInt       unsplit;
9170     PetscBool      ignoreZeroVol = PETSC_FALSE;
9171 
9172     PetscCall(DMPlexGetCellType(dm, c, &ct));
9173     switch (ct) {
9174     case DM_POLYTOPE_SEG_PRISM_TENSOR:
9175     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9176     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9177       ignoreZeroVol = PETSC_TRUE;
9178       break;
9179     default:
9180       break;
9181     }
9182     switch (ct) {
9183     case DM_POLYTOPE_TRI_PRISM:
9184     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9185     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9186     case DM_POLYTOPE_PYRAMID:
9187       continue;
9188     default:
9189       break;
9190     }
9191     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9192     if (unsplit) continue;
9193     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
9194     PetscCheck(detJ >= -PETSC_SMALL && (detJ > 0.0 || ignoreZeroVol), PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " of type %s is inverted, |J| = %g", c, DMPolytopeTypes[ct], (double)detJ);
9195     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
9196     /* This should work with periodicity since DG coordinates should be used */
9197     if (depth > 1) {
9198       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
9199       PetscCheck(vol >= -PETSC_SMALL && (vol > 0.0 || ignoreZeroVol), PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " of type %s is inverted, vol = %g", c, DMPolytopeTypes[ct], (double)vol);
9200       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
9201     }
9202   }
9203   PetscFunctionReturn(PETSC_SUCCESS);
9204 }
9205 
9206 /*@
9207   DMPlexCheckPointSF - Check that several necessary conditions are met for the point `PetscSF` of this plex.
9208 
9209   Collective
9210 
9211   Input Parameters:
9212 + dm              - The `DMPLEX` object
9213 . pointSF         - The `PetscSF`, or `NULL` for `PointSF` attached to `DM`
9214 - allowExtraRoots - Flag to allow extra points not present in the `DM`
9215 
9216   Level: developer
9217 
9218   Notes:
9219   This is mainly intended for debugging/testing purposes.
9220 
9221   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9222 
9223   Extra roots can come from periodic cuts, where additional points appear on the boundary
9224 
9225 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
9226 @*/
9227 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
9228 {
9229   PetscInt           l, nleaves, nroots, overlap;
9230   const PetscInt    *locals;
9231   const PetscSFNode *remotes;
9232   PetscBool          distributed;
9233   MPI_Comm           comm;
9234   PetscMPIInt        rank;
9235 
9236   PetscFunctionBegin;
9237   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9238   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
9239   else pointSF = dm->sf;
9240   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9241   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
9242   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9243   {
9244     PetscMPIInt mpiFlag;
9245 
9246     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
9247     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
9248   }
9249   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
9250   PetscCall(DMPlexIsDistributed(dm, &distributed));
9251   if (!distributed) {
9252     PetscCheck(nroots < 0 || nleaves == 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Undistributed DMPlex cannot have non-empty PointSF (has %" PetscInt_FMT " roots, %" PetscInt_FMT " leaves)", nroots, nleaves);
9253     PetscFunctionReturn(PETSC_SUCCESS);
9254   }
9255   PetscCheck(nroots >= 0, comm, PETSC_ERR_ARG_WRONGSTATE, "This DMPlex is distributed but its PointSF has no graph set (has %" PetscInt_FMT " roots, %" PetscInt_FMT " leaves)", nroots, nleaves);
9256   PetscCall(DMPlexGetOverlap(dm, &overlap));
9257 
9258   /* Check SF graph is compatible with DMPlex chart */
9259   {
9260     PetscInt pStart, pEnd, maxLeaf;
9261 
9262     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9263     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9264     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9265     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9266   }
9267 
9268   /* Check Point SF has no local points referenced */
9269   for (l = 0; l < nleaves; l++) {
9270     PetscAssert(remotes[l].rank != (PetscInt)rank, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains local point %" PetscInt_FMT " <- (%" PetscInt_FMT ",%" PetscInt_FMT ")", locals ? locals[l] : l, remotes[l].rank, remotes[l].index);
9271   }
9272 
9273   /* Check there are no cells in interface */
9274   if (!overlap) {
9275     PetscInt cellHeight, cStart, cEnd;
9276 
9277     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9278     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9279     for (l = 0; l < nleaves; ++l) {
9280       const PetscInt point = locals ? locals[l] : l;
9281 
9282       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9283     }
9284   }
9285 
9286   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9287   {
9288     const PetscInt *rootdegree;
9289 
9290     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9291     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9292     for (l = 0; l < nleaves; ++l) {
9293       const PetscInt  point = locals ? locals[l] : l;
9294       const PetscInt *cone;
9295       PetscInt        coneSize, c, idx;
9296 
9297       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9298       PetscCall(DMPlexGetCone(dm, point, &cone));
9299       for (c = 0; c < coneSize; ++c) {
9300         if (!rootdegree[cone[c]]) {
9301           if (locals) {
9302             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9303           } else {
9304             idx = (cone[c] < nleaves) ? cone[c] : -1;
9305           }
9306           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9307         }
9308       }
9309     }
9310   }
9311   PetscFunctionReturn(PETSC_SUCCESS);
9312 }
9313 
9314 /*@
9315   DMPlexCheck - Perform various checks of `DMPLEX` sanity
9316 
9317   Input Parameter:
9318 . dm - The `DMPLEX` object
9319 
9320   Level: developer
9321 
9322   Notes:
9323   This is a useful diagnostic when creating meshes programmatically.
9324 
9325   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9326 
9327   Currently does not include `DMPlexCheckCellShape()`.
9328 
9329 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9330 @*/
9331 PetscErrorCode DMPlexCheck(DM dm)
9332 {
9333   PetscInt cellHeight;
9334 
9335   PetscFunctionBegin;
9336   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9337   PetscCall(DMPlexCheckSymmetry(dm));
9338   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9339   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9340   PetscCall(DMPlexCheckGeometry(dm));
9341   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9342   PetscCall(DMPlexCheckInterfaceCones(dm));
9343   PetscFunctionReturn(PETSC_SUCCESS);
9344 }
9345 
9346 typedef struct cell_stats {
9347   PetscReal min, max, sum, squaresum;
9348   PetscInt  count;
9349 } cell_stats_t;
9350 
9351 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9352 {
9353   PetscInt i, N = *len;
9354 
9355   for (i = 0; i < N; i++) {
9356     cell_stats_t *A = (cell_stats_t *)a;
9357     cell_stats_t *B = (cell_stats_t *)b;
9358 
9359     B->min = PetscMin(A->min, B->min);
9360     B->max = PetscMax(A->max, B->max);
9361     B->sum += A->sum;
9362     B->squaresum += A->squaresum;
9363     B->count += A->count;
9364   }
9365 }
9366 
9367 /*@
9368   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9369 
9370   Collective
9371 
9372   Input Parameters:
9373 + dm        - The `DMPLEX` object
9374 . output    - If true, statistics will be displayed on `stdout`
9375 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9376 
9377   Level: developer
9378 
9379   Notes:
9380   This is mainly intended for debugging/testing purposes.
9381 
9382   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9383 
9384 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9385 @*/
9386 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9387 {
9388   DM           dmCoarse;
9389   cell_stats_t stats, globalStats;
9390   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9391   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9392   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9393   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9394   PetscMPIInt  rank, size;
9395 
9396   PetscFunctionBegin;
9397   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9398   stats.min = PETSC_MAX_REAL;
9399   stats.max = PETSC_MIN_REAL;
9400   stats.sum = stats.squaresum = 0.;
9401   stats.count                 = 0;
9402 
9403   PetscCallMPI(MPI_Comm_size(comm, &size));
9404   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9405   PetscCall(DMGetCoordinateDim(dm, &cdim));
9406   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9407   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9408   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9409   for (c = cStart; c < cEnd; c++) {
9410     PetscInt  i;
9411     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9412 
9413     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9414     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9415     for (i = 0; i < PetscSqr(cdim); ++i) {
9416       frobJ += J[i] * J[i];
9417       frobInvJ += invJ[i] * invJ[i];
9418     }
9419     cond2 = frobJ * frobInvJ;
9420     cond  = PetscSqrtReal(cond2);
9421 
9422     stats.min = PetscMin(stats.min, cond);
9423     stats.max = PetscMax(stats.max, cond);
9424     stats.sum += cond;
9425     stats.squaresum += cond2;
9426     stats.count++;
9427     if (output && cond > limit) {
9428       PetscSection coordSection;
9429       Vec          coordsLocal;
9430       PetscScalar *coords = NULL;
9431       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9432 
9433       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9434       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9435       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9436       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9437       for (i = 0; i < Nv / cdim; ++i) {
9438         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9439         for (d = 0; d < cdim; ++d) {
9440           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9441           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9442         }
9443         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9444       }
9445       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9446       for (cl = 0; cl < clSize * 2; cl += 2) {
9447         const PetscInt edge = closure[cl];
9448 
9449         if ((edge >= eStart) && (edge < eEnd)) {
9450           PetscReal len;
9451 
9452           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9453           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9454         }
9455       }
9456       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9457       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9458     }
9459   }
9460   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9461 
9462   if (size > 1) {
9463     PetscMPIInt  blockLengths[2] = {4, 1};
9464     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9465     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9466     MPI_Op       statReduce;
9467 
9468     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9469     PetscCallMPI(MPI_Type_commit(&statType));
9470     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9471     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9472     PetscCallMPI(MPI_Op_free(&statReduce));
9473     PetscCallMPI(MPI_Type_free(&statType));
9474   } else {
9475     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9476   }
9477   if (rank == 0) {
9478     count = globalStats.count;
9479     min   = globalStats.min;
9480     max   = globalStats.max;
9481     mean  = globalStats.sum / globalStats.count;
9482     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9483   }
9484 
9485   if (output) PetscCall(PetscPrintf(comm, "Mesh with %" PetscInt_FMT " cells, shape condition numbers: min = %g, max = %g, mean = %g, stddev = %g\n", count, (double)min, (double)max, (double)mean, (double)stdev));
9486   PetscCall(PetscFree2(J, invJ));
9487 
9488   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9489   if (dmCoarse) {
9490     PetscBool isplex;
9491 
9492     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9493     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9494   }
9495   PetscFunctionReturn(PETSC_SUCCESS);
9496 }
9497 
9498 /*@
9499   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9500   orthogonal quality below given tolerance.
9501 
9502   Collective
9503 
9504   Input Parameters:
9505 + dm   - The `DMPLEX` object
9506 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9507 - atol - [0, 1] Absolute tolerance for tagging cells.
9508 
9509   Output Parameters:
9510 + OrthQual      - `Vec` containing orthogonal quality per cell
9511 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9512 
9513   Options Database Keys:
9514 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9515 - -dm_plex_orthogonal_quality_vec_view   - view OrthQual vector.
9516 
9517   Level: intermediate
9518 
9519   Notes:
9520   Orthogonal quality is given by the following formula\:
9521 
9522   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9523 
9524   Where A_i is the i'th face-normal vector, f_i is the vector from the cell centroid to the i'th face centroid, and c_i
9525   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9526   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9527   calculating the cosine of the angle between these vectors.
9528 
9529   Orthogonal quality ranges from 1 (best) to 0 (worst).
9530 
9531   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9532   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9533 
9534   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9535 
9536 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9537 @*/
9538 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9539 {
9540   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9541   PetscInt              *idx;
9542   PetscScalar           *oqVals;
9543   const PetscScalar     *cellGeomArr, *faceGeomArr;
9544   PetscReal             *ci, *fi, *Ai;
9545   MPI_Comm               comm;
9546   Vec                    cellgeom, facegeom;
9547   DM                     dmFace, dmCell;
9548   IS                     glob;
9549   ISLocalToGlobalMapping ltog;
9550   PetscViewer            vwr;
9551 
9552   PetscFunctionBegin;
9553   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9554   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9555   PetscAssertPointer(OrthQual, 4);
9556   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9557   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9558   PetscCall(DMGetDimension(dm, &nc));
9559   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9560   {
9561     DMPlexInterpolatedFlag interpFlag;
9562 
9563     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9564     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9565       PetscMPIInt rank;
9566 
9567       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9568       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9569     }
9570   }
9571   if (OrthQualLabel) {
9572     PetscAssertPointer(OrthQualLabel, 5);
9573     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9574     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9575   } else {
9576     *OrthQualLabel = NULL;
9577   }
9578   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9579   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9580   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
9581   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9582   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9583   PetscCall(VecCreate(comm, OrthQual));
9584   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9585   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9586   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9587   PetscCall(VecSetUp(*OrthQual));
9588   PetscCall(ISDestroy(&glob));
9589   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9590   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9591   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9592   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9593   PetscCall(VecGetDM(cellgeom, &dmCell));
9594   PetscCall(VecGetDM(facegeom, &dmFace));
9595   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9596   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
9597     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9598     PetscInt         cellarr[2], *adj = NULL;
9599     PetscScalar     *cArr, *fArr;
9600     PetscReal        minvalc = 1.0, minvalf = 1.0;
9601     PetscFVCellGeom *cg;
9602 
9603     idx[cellIter] = cell - cStart;
9604     cellarr[0]    = cell;
9605     /* Make indexing into cellGeom easier */
9606     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9607     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9608     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9609     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9610     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
9611       PetscInt         i;
9612       const PetscInt   neigh  = adj[cellneigh];
9613       PetscReal        normci = 0, normfi = 0, normai = 0;
9614       PetscFVCellGeom *cgneigh;
9615       PetscFVFaceGeom *fg;
9616 
9617       /* Don't count ourselves in the neighbor list */
9618       if (neigh == cell) continue;
9619       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9620       cellarr[1] = neigh;
9621       {
9622         PetscInt        numcovpts;
9623         const PetscInt *covpts;
9624 
9625         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9626         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9627         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9628       }
9629 
9630       /* Compute c_i, f_i and their norms */
9631       for (i = 0; i < nc; i++) {
9632         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9633         fi[i] = fg->centroid[i] - cg->centroid[i];
9634         Ai[i] = fg->normal[i];
9635         normci += PetscPowReal(ci[i], 2);
9636         normfi += PetscPowReal(fi[i], 2);
9637         normai += PetscPowReal(Ai[i], 2);
9638       }
9639       normci = PetscSqrtReal(normci);
9640       normfi = PetscSqrtReal(normfi);
9641       normai = PetscSqrtReal(normai);
9642 
9643       /* Normalize and compute for each face-cell-normal pair */
9644       for (i = 0; i < nc; i++) {
9645         ci[i] = ci[i] / normci;
9646         fi[i] = fi[i] / normfi;
9647         Ai[i] = Ai[i] / normai;
9648         /* PetscAbs because I don't know if normals are guaranteed to point out */
9649         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
9650         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
9651       }
9652       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
9653       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
9654     }
9655     PetscCall(PetscFree(adj));
9656     PetscCall(PetscFree2(cArr, fArr));
9657     /* Defer to cell if they're equal */
9658     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9659     if (OrthQualLabel) {
9660       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9661     }
9662   }
9663   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
9664   PetscCall(VecAssemblyBegin(*OrthQual));
9665   PetscCall(VecAssemblyEnd(*OrthQual));
9666   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9667   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9668   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9669   if (OrthQualLabel) {
9670     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9671   }
9672   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9673   PetscCall(PetscViewerDestroy(&vwr));
9674   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9675   PetscFunctionReturn(PETSC_SUCCESS);
9676 }
9677 
9678 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9679  * interpolator construction */
9680 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9681 {
9682   PetscSection section, newSection, gsection;
9683   PetscSF      sf;
9684   PetscBool    hasConstraints, ghasConstraints;
9685 
9686   PetscFunctionBegin;
9687   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9688   PetscAssertPointer(odm, 2);
9689   PetscCall(DMGetLocalSection(dm, &section));
9690   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9691   PetscCall(MPIU_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
9692   if (!ghasConstraints) {
9693     PetscCall(PetscObjectReference((PetscObject)dm));
9694     *odm = dm;
9695     PetscFunctionReturn(PETSC_SUCCESS);
9696   }
9697   PetscCall(DMClone(dm, odm));
9698   PetscCall(DMCopyFields(dm, *odm));
9699   PetscCall(DMGetLocalSection(*odm, &newSection));
9700   PetscCall(DMGetPointSF(*odm, &sf));
9701   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_FALSE, &gsection));
9702   PetscCall(DMSetGlobalSection(*odm, gsection));
9703   PetscCall(PetscSectionDestroy(&gsection));
9704   PetscFunctionReturn(PETSC_SUCCESS);
9705 }
9706 
9707 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9708 {
9709   DM        dmco, dmfo;
9710   Mat       interpo;
9711   Vec       rscale;
9712   Vec       cglobalo, clocal;
9713   Vec       fglobal, fglobalo, flocal;
9714   PetscBool regular;
9715 
9716   PetscFunctionBegin;
9717   PetscCall(DMGetFullDM(dmc, &dmco));
9718   PetscCall(DMGetFullDM(dmf, &dmfo));
9719   PetscCall(DMSetCoarseDM(dmfo, dmco));
9720   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9721   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9722   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9723   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9724   PetscCall(DMCreateLocalVector(dmc, &clocal));
9725   PetscCall(VecSet(cglobalo, 0.));
9726   PetscCall(VecSet(clocal, 0.));
9727   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9728   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9729   PetscCall(DMCreateLocalVector(dmf, &flocal));
9730   PetscCall(VecSet(fglobal, 0.));
9731   PetscCall(VecSet(fglobalo, 0.));
9732   PetscCall(VecSet(flocal, 0.));
9733   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9734   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9735   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9736   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9737   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9738   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9739   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9740   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9741   *shift = fglobal;
9742   PetscCall(VecDestroy(&flocal));
9743   PetscCall(VecDestroy(&fglobalo));
9744   PetscCall(VecDestroy(&clocal));
9745   PetscCall(VecDestroy(&cglobalo));
9746   PetscCall(VecDestroy(&rscale));
9747   PetscCall(MatDestroy(&interpo));
9748   PetscCall(DMDestroy(&dmfo));
9749   PetscCall(DMDestroy(&dmco));
9750   PetscFunctionReturn(PETSC_SUCCESS);
9751 }
9752 
9753 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
9754 {
9755   PetscObject shifto;
9756   Vec         shift;
9757 
9758   PetscFunctionBegin;
9759   if (!interp) {
9760     Vec rscale;
9761 
9762     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9763     PetscCall(VecDestroy(&rscale));
9764   } else {
9765     PetscCall(PetscObjectReference((PetscObject)interp));
9766   }
9767   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9768   if (!shifto) {
9769     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9770     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
9771     shifto = (PetscObject)shift;
9772     PetscCall(VecDestroy(&shift));
9773   }
9774   shift = (Vec)shifto;
9775   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9776   PetscCall(VecAXPY(fineSol, 1.0, shift));
9777   PetscCall(MatDestroy(&interp));
9778   PetscFunctionReturn(PETSC_SUCCESS);
9779 }
9780 
9781 /* Pointwise interpolation
9782      Just code FEM for now
9783      u^f = I u^c
9784      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9785      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9786      I_{ij} = psi^f_i phi^c_j
9787 */
9788 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
9789 {
9790   PetscSection gsc, gsf;
9791   PetscInt     m, n;
9792   void        *ctx;
9793   DM           cdm;
9794   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9795 
9796   PetscFunctionBegin;
9797   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9798   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9799   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9800   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9801 
9802   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9803   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
9804   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9805   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9806   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9807 
9808   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9809   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9810   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9811   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9812   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9813   if (scaling) {
9814     /* Use naive scaling */
9815     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
9816   }
9817   PetscFunctionReturn(PETSC_SUCCESS);
9818 }
9819 
9820 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
9821 {
9822   VecScatter ctx;
9823 
9824   PetscFunctionBegin;
9825   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
9826   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
9827   PetscCall(VecScatterDestroy(&ctx));
9828   PetscFunctionReturn(PETSC_SUCCESS);
9829 }
9830 
9831 static void g0_identity_private(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, PetscReal u_tShift, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar g0[])
9832 {
9833   const PetscInt Nc = uOff[1] - uOff[0];
9834   PetscInt       c;
9835   for (c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
9836 }
9837 
9838 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
9839 {
9840   DM           dmc;
9841   PetscDS      ds;
9842   Vec          ones, locmass;
9843   IS           cellIS;
9844   PetscFormKey key;
9845   PetscInt     depth;
9846 
9847   PetscFunctionBegin;
9848   PetscCall(DMClone(dm, &dmc));
9849   PetscCall(DMCopyDisc(dm, dmc));
9850   PetscCall(DMGetDS(dmc, &ds));
9851   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9852   PetscCall(DMCreateGlobalVector(dmc, mass));
9853   PetscCall(DMGetLocalVector(dmc, &ones));
9854   PetscCall(DMGetLocalVector(dmc, &locmass));
9855   PetscCall(DMPlexGetDepth(dmc, &depth));
9856   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9857   PetscCall(VecSet(locmass, 0.0));
9858   PetscCall(VecSet(ones, 1.0));
9859   key.label = NULL;
9860   key.value = 0;
9861   key.field = 0;
9862   key.part  = 0;
9863   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
9864   PetscCall(ISDestroy(&cellIS));
9865   PetscCall(VecSet(*mass, 0.0));
9866   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
9867   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
9868   PetscCall(DMRestoreLocalVector(dmc, &ones));
9869   PetscCall(DMRestoreLocalVector(dmc, &locmass));
9870   PetscCall(DMDestroy(&dmc));
9871   PetscFunctionReturn(PETSC_SUCCESS);
9872 }
9873 
9874 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
9875 {
9876   PetscSection gsc, gsf;
9877   PetscInt     m, n;
9878   void        *ctx;
9879   DM           cdm;
9880   PetscBool    regular;
9881 
9882   PetscFunctionBegin;
9883   if (dmFine == dmCoarse) {
9884     DM            dmc;
9885     PetscDS       ds;
9886     PetscWeakForm wf;
9887     Vec           u;
9888     IS            cellIS;
9889     PetscFormKey  key;
9890     PetscInt      depth;
9891 
9892     PetscCall(DMClone(dmFine, &dmc));
9893     PetscCall(DMCopyDisc(dmFine, dmc));
9894     PetscCall(DMGetDS(dmc, &ds));
9895     PetscCall(PetscDSGetWeakForm(ds, &wf));
9896     PetscCall(PetscWeakFormClear(wf));
9897     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9898     PetscCall(DMCreateMatrix(dmc, mass));
9899     PetscCall(DMGetLocalVector(dmc, &u));
9900     PetscCall(DMPlexGetDepth(dmc, &depth));
9901     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9902     PetscCall(MatZeroEntries(*mass));
9903     key.label = NULL;
9904     key.value = 0;
9905     key.field = 0;
9906     key.part  = 0;
9907     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
9908     PetscCall(ISDestroy(&cellIS));
9909     PetscCall(DMRestoreLocalVector(dmc, &u));
9910     PetscCall(DMDestroy(&dmc));
9911   } else {
9912     PetscCall(DMGetGlobalSection(dmFine, &gsf));
9913     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9914     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9915     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9916 
9917     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
9918     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9919     PetscCall(MatSetType(*mass, dmCoarse->mattype));
9920     PetscCall(DMGetApplicationContext(dmFine, &ctx));
9921 
9922     PetscCall(DMGetCoarseDM(dmFine, &cdm));
9923     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9924     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
9925     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
9926   }
9927   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
9928   PetscFunctionReturn(PETSC_SUCCESS);
9929 }
9930 
9931 /*@
9932   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9933 
9934   Input Parameter:
9935 . dm - The `DMPLEX` object
9936 
9937   Output Parameter:
9938 . regular - The flag
9939 
9940   Level: intermediate
9941 
9942 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
9943 @*/
9944 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
9945 {
9946   PetscFunctionBegin;
9947   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9948   PetscAssertPointer(regular, 2);
9949   *regular = ((DM_Plex *)dm->data)->regularRefinement;
9950   PetscFunctionReturn(PETSC_SUCCESS);
9951 }
9952 
9953 /*@
9954   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9955 
9956   Input Parameters:
9957 + dm      - The `DMPLEX` object
9958 - regular - The flag
9959 
9960   Level: intermediate
9961 
9962 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
9963 @*/
9964 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
9965 {
9966   PetscFunctionBegin;
9967   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9968   ((DM_Plex *)dm->data)->regularRefinement = regular;
9969   PetscFunctionReturn(PETSC_SUCCESS);
9970 }
9971 
9972 /*@
9973   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
9974   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
9975 
9976   Not Collective
9977 
9978   Input Parameter:
9979 . dm - The `DMPLEX` object
9980 
9981   Output Parameters:
9982 + anchorSection - If not `NULL`, set to the section describing which points anchor the constrained points.
9983 - anchorIS      - If not `NULL`, set to the list of anchors indexed by `anchorSection`
9984 
9985   Level: intermediate
9986 
9987 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
9988 @*/
9989 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
9990 {
9991   DM_Plex *plex = (DM_Plex *)dm->data;
9992 
9993   PetscFunctionBegin;
9994   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9995   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
9996   if (anchorSection) *anchorSection = plex->anchorSection;
9997   if (anchorIS) *anchorIS = plex->anchorIS;
9998   PetscFunctionReturn(PETSC_SUCCESS);
9999 }
10000 
10001 /*@
10002   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.
10003 
10004   Collective
10005 
10006   Input Parameters:
10007 + dm            - The `DMPLEX` object
10008 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
10009                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10010 - anchorIS      - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10011 
10012   Level: intermediate
10013 
10014   Notes:
10015   Unlike boundary conditions, when a point's degrees of freedom in a section are constrained to
10016   an outside value, the anchor constraints set a point's degrees of freedom to be a linear
10017   combination of other points' degrees of freedom.
10018 
10019   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
10020   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
10021 
10022   The reference counts of `anchorSection` and `anchorIS` are incremented.
10023 
10024 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
10025 @*/
10026 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
10027 {
10028   DM_Plex    *plex = (DM_Plex *)dm->data;
10029   PetscMPIInt result;
10030 
10031   PetscFunctionBegin;
10032   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10033   if (anchorSection) {
10034     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
10035     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
10036     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
10037   }
10038   if (anchorIS) {
10039     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
10040     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
10041     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
10042   }
10043 
10044   PetscCall(PetscObjectReference((PetscObject)anchorSection));
10045   PetscCall(PetscSectionDestroy(&plex->anchorSection));
10046   plex->anchorSection = anchorSection;
10047 
10048   PetscCall(PetscObjectReference((PetscObject)anchorIS));
10049   PetscCall(ISDestroy(&plex->anchorIS));
10050   plex->anchorIS = anchorIS;
10051 
10052   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
10053     PetscInt        size, a, pStart, pEnd;
10054     const PetscInt *anchors;
10055 
10056     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10057     PetscCall(ISGetLocalSize(anchorIS, &size));
10058     PetscCall(ISGetIndices(anchorIS, &anchors));
10059     for (a = 0; a < size; a++) {
10060       PetscInt p;
10061 
10062       p = anchors[a];
10063       if (p >= pStart && p < pEnd) {
10064         PetscInt dof;
10065 
10066         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10067         if (dof) {
10068           PetscCall(ISRestoreIndices(anchorIS, &anchors));
10069           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
10070         }
10071       }
10072     }
10073     PetscCall(ISRestoreIndices(anchorIS, &anchors));
10074   }
10075   /* reset the generic constraints */
10076   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
10077   PetscFunctionReturn(PETSC_SUCCESS);
10078 }
10079 
10080 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
10081 {
10082   PetscSection anchorSection;
10083   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
10084 
10085   PetscFunctionBegin;
10086   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10087   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10088   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
10089   PetscCall(PetscSectionGetNumFields(section, &numFields));
10090   if (numFields) {
10091     PetscInt f;
10092     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
10093 
10094     for (f = 0; f < numFields; f++) {
10095       PetscInt numComp;
10096 
10097       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
10098       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
10099     }
10100   }
10101   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10102   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10103   pStart = PetscMax(pStart, sStart);
10104   pEnd   = PetscMin(pEnd, sEnd);
10105   pEnd   = PetscMax(pStart, pEnd);
10106   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
10107   for (p = pStart; p < pEnd; p++) {
10108     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10109     if (dof) {
10110       PetscCall(PetscSectionGetDof(section, p, &dof));
10111       PetscCall(PetscSectionSetDof(*cSec, p, dof));
10112       for (f = 0; f < numFields; f++) {
10113         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
10114         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
10115       }
10116     }
10117   }
10118   PetscCall(PetscSectionSetUp(*cSec));
10119   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
10120   PetscFunctionReturn(PETSC_SUCCESS);
10121 }
10122 
10123 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
10124 {
10125   PetscSection    aSec;
10126   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
10127   const PetscInt *anchors;
10128   PetscInt        numFields, f;
10129   IS              aIS;
10130   MatType         mtype;
10131   PetscBool       iscuda, iskokkos;
10132 
10133   PetscFunctionBegin;
10134   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10135   PetscCall(PetscSectionGetStorageSize(cSec, &m));
10136   PetscCall(PetscSectionGetStorageSize(section, &n));
10137   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
10138   PetscCall(MatSetSizes(*cMat, m, n, m, n));
10139   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
10140   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
10141   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
10142   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
10143   if (iscuda) mtype = MATSEQAIJCUSPARSE;
10144   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
10145   else mtype = MATSEQAIJ;
10146   PetscCall(MatSetType(*cMat, mtype));
10147   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
10148   PetscCall(ISGetIndices(aIS, &anchors));
10149   /* cSec will be a subset of aSec and section */
10150   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
10151   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10152   PetscCall(PetscMalloc1(m + 1, &i));
10153   i[0] = 0;
10154   PetscCall(PetscSectionGetNumFields(section, &numFields));
10155   for (p = pStart; p < pEnd; p++) {
10156     PetscInt rDof, rOff, r;
10157 
10158     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10159     if (!rDof) continue;
10160     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10161     if (numFields) {
10162       for (f = 0; f < numFields; f++) {
10163         annz = 0;
10164         for (r = 0; r < rDof; r++) {
10165           a = anchors[rOff + r];
10166           if (a < sStart || a >= sEnd) continue;
10167           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10168           annz += aDof;
10169         }
10170         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10171         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
10172         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10173       }
10174     } else {
10175       annz = 0;
10176       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10177       for (q = 0; q < dof; q++) {
10178         a = anchors[rOff + q];
10179         if (a < sStart || a >= sEnd) continue;
10180         PetscCall(PetscSectionGetDof(section, a, &aDof));
10181         annz += aDof;
10182       }
10183       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10184       PetscCall(PetscSectionGetOffset(cSec, p, &off));
10185       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10186     }
10187   }
10188   nnz = i[m];
10189   PetscCall(PetscMalloc1(nnz, &j));
10190   offset = 0;
10191   for (p = pStart; p < pEnd; p++) {
10192     if (numFields) {
10193       for (f = 0; f < numFields; f++) {
10194         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10195         for (q = 0; q < dof; q++) {
10196           PetscInt rDof, rOff, r;
10197           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10198           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10199           for (r = 0; r < rDof; r++) {
10200             PetscInt s;
10201 
10202             a = anchors[rOff + r];
10203             if (a < sStart || a >= sEnd) continue;
10204             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10205             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
10206             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10207           }
10208         }
10209       }
10210     } else {
10211       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10212       for (q = 0; q < dof; q++) {
10213         PetscInt rDof, rOff, r;
10214         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10215         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10216         for (r = 0; r < rDof; r++) {
10217           PetscInt s;
10218 
10219           a = anchors[rOff + r];
10220           if (a < sStart || a >= sEnd) continue;
10221           PetscCall(PetscSectionGetDof(section, a, &aDof));
10222           PetscCall(PetscSectionGetOffset(section, a, &aOff));
10223           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10224         }
10225       }
10226     }
10227   }
10228   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
10229   PetscCall(PetscFree(i));
10230   PetscCall(PetscFree(j));
10231   PetscCall(ISRestoreIndices(aIS, &anchors));
10232   PetscFunctionReturn(PETSC_SUCCESS);
10233 }
10234 
10235 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
10236 {
10237   DM_Plex     *plex = (DM_Plex *)dm->data;
10238   PetscSection anchorSection, section, cSec;
10239   Mat          cMat;
10240 
10241   PetscFunctionBegin;
10242   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10243   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10244   if (anchorSection) {
10245     PetscInt Nf;
10246 
10247     PetscCall(DMGetLocalSection(dm, &section));
10248     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
10249     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
10250     PetscCall(DMGetNumFields(dm, &Nf));
10251     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
10252     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
10253     PetscCall(PetscSectionDestroy(&cSec));
10254     PetscCall(MatDestroy(&cMat));
10255   }
10256   PetscFunctionReturn(PETSC_SUCCESS);
10257 }
10258 
10259 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10260 {
10261   IS           subis;
10262   PetscSection section, subsection;
10263 
10264   PetscFunctionBegin;
10265   PetscCall(DMGetLocalSection(dm, &section));
10266   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10267   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10268   /* Create subdomain */
10269   PetscCall(DMPlexFilter(dm, label, value, subdm));
10270   /* Create submodel */
10271   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10272   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10273   PetscCall(DMSetLocalSection(*subdm, subsection));
10274   PetscCall(PetscSectionDestroy(&subsection));
10275   PetscCall(DMCopyDisc(dm, *subdm));
10276   /* Create map from submodel to global model */
10277   if (is) {
10278     PetscSection    sectionGlobal, subsectionGlobal;
10279     IS              spIS;
10280     const PetscInt *spmap;
10281     PetscInt       *subIndices;
10282     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10283     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10284 
10285     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10286     PetscCall(ISGetIndices(spIS, &spmap));
10287     PetscCall(PetscSectionGetNumFields(section, &Nf));
10288     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10289     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10290     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10291     for (p = pStart; p < pEnd; ++p) {
10292       PetscInt gdof, pSubSize = 0;
10293 
10294       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10295       if (gdof > 0) {
10296         for (f = 0; f < Nf; ++f) {
10297           PetscInt fdof, fcdof;
10298 
10299           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10300           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10301           pSubSize += fdof - fcdof;
10302         }
10303         subSize += pSubSize;
10304         if (pSubSize) {
10305           if (bs < 0) {
10306             bs = pSubSize;
10307           } else if (bs != pSubSize) {
10308             /* Layout does not admit a pointwise block size */
10309             bs = 1;
10310           }
10311         }
10312       }
10313     }
10314     /* Must have same blocksize on all procs (some might have no points) */
10315     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
10316     bsLocal[1] = bs;
10317     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10318     if (bsMinMax[0] != bsMinMax[1]) {
10319       bs = 1;
10320     } else {
10321       bs = bsMinMax[0];
10322     }
10323     PetscCall(PetscMalloc1(subSize, &subIndices));
10324     for (p = pStart; p < pEnd; ++p) {
10325       PetscInt gdof, goff;
10326 
10327       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10328       if (gdof > 0) {
10329         const PetscInt point = spmap[p];
10330 
10331         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10332         for (f = 0; f < Nf; ++f) {
10333           PetscInt fdof, fcdof, fc, f2, poff = 0;
10334 
10335           /* Can get rid of this loop by storing field information in the global section */
10336           for (f2 = 0; f2 < f; ++f2) {
10337             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10338             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10339             poff += fdof - fcdof;
10340           }
10341           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10342           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10343           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10344         }
10345       }
10346     }
10347     PetscCall(ISRestoreIndices(spIS, &spmap));
10348     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10349     if (bs > 1) {
10350       /* We need to check that the block size does not come from non-contiguous fields */
10351       PetscInt i, j, set = 1;
10352       for (i = 0; i < subSize; i += bs) {
10353         for (j = 0; j < bs; ++j) {
10354           if (subIndices[i + j] != subIndices[i] + j) {
10355             set = 0;
10356             break;
10357           }
10358         }
10359       }
10360       if (set) PetscCall(ISSetBlockSize(*is, bs));
10361     }
10362     /* Attach nullspace */
10363     for (f = 0; f < Nf; ++f) {
10364       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10365       if ((*subdm)->nullspaceConstructors[f]) break;
10366     }
10367     if (f < Nf) {
10368       MatNullSpace nullSpace;
10369       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10370 
10371       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10372       PetscCall(MatNullSpaceDestroy(&nullSpace));
10373     }
10374   }
10375   PetscFunctionReturn(PETSC_SUCCESS);
10376 }
10377 
10378 /*@
10379   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10380 
10381   Input Parameters:
10382 + dm    - The `DM`
10383 - dummy - unused argument
10384 
10385   Options Database Key:
10386 . -dm_plex_monitor_throughput - Activate the monitor
10387 
10388   Level: developer
10389 
10390 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10391 @*/
10392 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10393 {
10394   PetscLogHandler default_handler;
10395 
10396   PetscFunctionBegin;
10397   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10398   PetscCall(PetscLogGetDefaultHandler(&default_handler));
10399   if (default_handler) {
10400     PetscLogEvent      event;
10401     PetscEventPerfInfo eventInfo;
10402     PetscReal          cellRate, flopRate;
10403     PetscInt           cStart, cEnd, Nf, N;
10404     const char        *name;
10405 
10406     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10407     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10408     PetscCall(DMGetNumFields(dm, &Nf));
10409     PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10410     PetscCall(PetscLogEventGetPerfInfo(PETSC_DEFAULT, event, &eventInfo));
10411     N        = (cEnd - cStart) * Nf * eventInfo.count;
10412     flopRate = eventInfo.flops / eventInfo.time;
10413     cellRate = N / eventInfo.time;
10414     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DM (%s) FE Residual Integration: %" PetscInt_FMT " integrals %d reps\n  Cell rate: %.2g/s flop rate: %.2g MF/s\n", name ? name : "unknown", N, eventInfo.count, (double)cellRate, (double)(flopRate / 1.e6)));
10415   } else {
10416     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Plex Throughput Monitor is not supported if logging is turned off or the default log handler is not running. Reconfigure using --with-log and run with -log_view.");
10417   }
10418   PetscFunctionReturn(PETSC_SUCCESS);
10419 }
10420