xref: /petsc/src/dm/impls/plex/plex.c (revision e44f6aeb0c7eee2716d50e70cd976c48a5d3c225)
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 /*
5602  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5603  representing a line in the section.
5604 */
5605 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(PetscSection section, PetscInt field, PetscInt line, PetscBool vertexchart, PetscInt *Nc, PetscInt *k)
5606 {
5607   PetscFunctionBeginHot;
5608   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5609   if (line < 0) {
5610     *k  = 0;
5611     *Nc = 0;
5612   } else if (vertexchart) { /* If we only have a vertex chart, we must have degree k=1 */
5613     *k = 1;
5614   } else { /* Assume the full interpolated mesh is in the chart; lines in particular */
5615     /* An order k SEM disc has k-1 dofs on an edge */
5616     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5617     *k = *k / *Nc + 1;
5618   }
5619   PetscFunctionReturn(PETSC_SUCCESS);
5620 }
5621 
5622 /*@
5623 
5624   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5625   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5626   section provided (or the section of the `DM`).
5627 
5628   Input Parameters:
5629 + dm      - The `DM`
5630 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or `PETSC_DETERMINE`
5631 - section - The `PetscSection` to reorder, or `NULL` for the default section
5632 
5633   Example:
5634   A typical interpolated single-quad mesh might order points as
5635 .vb
5636   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5637 
5638   v4 -- e6 -- v3
5639   |           |
5640   e7    c0    e8
5641   |           |
5642   v1 -- e5 -- v2
5643 .ve
5644 
5645   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5646   dofs in the order of points, e.g.,
5647 .vb
5648     c0 -> [0,1,2,3]
5649     v1 -> [4]
5650     ...
5651     e5 -> [8, 9]
5652 .ve
5653 
5654   which corresponds to the dofs
5655 .vb
5656     6   10  11  7
5657     13  2   3   15
5658     12  0   1   14
5659     4   8   9   5
5660 .ve
5661 
5662   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5663 .vb
5664   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5665 .ve
5666 
5667   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5668 .vb
5669    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5670 .ve
5671 
5672   Level: developer
5673 
5674   Notes:
5675   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5676   degree of the basis.
5677 
5678   This is required to run with libCEED.
5679 
5680 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5681 @*/
5682 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5683 {
5684   DMLabel   label;
5685   PetscInt  dim, depth = -1, eStart = -1, Nf;
5686   PetscBool vertexchart;
5687 
5688   PetscFunctionBegin;
5689   PetscCall(DMGetDimension(dm, &dim));
5690   if (dim < 1) PetscFunctionReturn(PETSC_SUCCESS);
5691   if (point < 0) {
5692     PetscInt sStart, sEnd;
5693 
5694     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5695     point = sEnd - sStart ? sStart : point;
5696   }
5697   PetscCall(DMPlexGetDepthLabel(dm, &label));
5698   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5699   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5700   if (depth == 1) {
5701     eStart = point;
5702   } else if (depth == dim) {
5703     const PetscInt *cone;
5704 
5705     PetscCall(DMPlexGetCone(dm, point, &cone));
5706     if (dim == 2) eStart = cone[0];
5707     else if (dim == 3) {
5708       const PetscInt *cone2;
5709       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5710       eStart = cone2[0];
5711     } 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);
5712   } 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);
5713   { /* Determine whether the chart covers all points or just vertices. */
5714     PetscInt pStart, pEnd, cStart, cEnd;
5715     PetscCall(DMPlexGetDepthStratum(dm, 0, &pStart, &pEnd));
5716     PetscCall(PetscSectionGetChart(section, &cStart, &cEnd));
5717     if (pStart == cStart && pEnd == cEnd) vertexchart = PETSC_TRUE;      /* Only vertices are in the chart */
5718     else if (cStart <= point && point < cEnd) vertexchart = PETSC_FALSE; /* Some interpolated points exist in the chart */
5719     else vertexchart = PETSC_TRUE;                                       /* Some interpolated points are not in chart; assume dofs only at cells and vertices */
5720   }
5721   PetscCall(PetscSectionGetNumFields(section, &Nf));
5722   for (PetscInt d = 1; d <= dim; d++) {
5723     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5724     PetscInt *perm;
5725 
5726     for (f = 0; f < Nf; ++f) {
5727       PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5728       size += PetscPowInt(k + 1, d) * Nc;
5729     }
5730     PetscCall(PetscMalloc1(size, &perm));
5731     for (f = 0; f < Nf; ++f) {
5732       switch (d) {
5733       case 1:
5734         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5735         /*
5736          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5737          We want              [ vtx0; edge of length k-1; vtx1 ]
5738          */
5739         for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
5740         for (i = 0; i < k - 1; i++)
5741           for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
5742         for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
5743         foffset = offset;
5744         break;
5745       case 2:
5746         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5747         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5748         /* The SEM order is
5749 
5750          v_lb, {e_b}, v_rb,
5751          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5752          v_lt, reverse {e_t}, v_rt
5753          */
5754         {
5755           const PetscInt of   = 0;
5756           const PetscInt oeb  = of + PetscSqr(k - 1);
5757           const PetscInt oer  = oeb + (k - 1);
5758           const PetscInt oet  = oer + (k - 1);
5759           const PetscInt oel  = oet + (k - 1);
5760           const PetscInt ovlb = oel + (k - 1);
5761           const PetscInt ovrb = ovlb + 1;
5762           const PetscInt ovrt = ovrb + 1;
5763           const PetscInt ovlt = ovrt + 1;
5764           PetscInt       o;
5765 
5766           /* bottom */
5767           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
5768           for (o = oeb; o < oer; ++o)
5769             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5770           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
5771           /* middle */
5772           for (i = 0; i < k - 1; ++i) {
5773             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
5774             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
5775               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5776             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
5777           }
5778           /* top */
5779           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
5780           for (o = oel - 1; o >= oet; --o)
5781             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5782           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
5783           foffset = offset;
5784         }
5785         break;
5786       case 3:
5787         /* The original hex closure is
5788 
5789          {c,
5790          f_b, f_t, f_f, f_b, f_r, f_l,
5791          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
5792          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
5793          */
5794         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5795         /* The SEM order is
5796          Bottom Slice
5797          v_blf, {e^{(k-1)-n}_bf}, v_brf,
5798          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
5799          v_blb, {e_bb}, v_brb,
5800 
5801          Middle Slice (j)
5802          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
5803          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
5804          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
5805 
5806          Top Slice
5807          v_tlf, {e_tf}, v_trf,
5808          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
5809          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
5810          */
5811         {
5812           const PetscInt oc    = 0;
5813           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
5814           const PetscInt oft   = ofb + PetscSqr(k - 1);
5815           const PetscInt off   = oft + PetscSqr(k - 1);
5816           const PetscInt ofk   = off + PetscSqr(k - 1);
5817           const PetscInt ofr   = ofk + PetscSqr(k - 1);
5818           const PetscInt ofl   = ofr + PetscSqr(k - 1);
5819           const PetscInt oebl  = ofl + PetscSqr(k - 1);
5820           const PetscInt oebb  = oebl + (k - 1);
5821           const PetscInt oebr  = oebb + (k - 1);
5822           const PetscInt oebf  = oebr + (k - 1);
5823           const PetscInt oetf  = oebf + (k - 1);
5824           const PetscInt oetr  = oetf + (k - 1);
5825           const PetscInt oetb  = oetr + (k - 1);
5826           const PetscInt oetl  = oetb + (k - 1);
5827           const PetscInt oerf  = oetl + (k - 1);
5828           const PetscInt oelf  = oerf + (k - 1);
5829           const PetscInt oelb  = oelf + (k - 1);
5830           const PetscInt oerb  = oelb + (k - 1);
5831           const PetscInt ovblf = oerb + (k - 1);
5832           const PetscInt ovblb = ovblf + 1;
5833           const PetscInt ovbrb = ovblb + 1;
5834           const PetscInt ovbrf = ovbrb + 1;
5835           const PetscInt ovtlf = ovbrf + 1;
5836           const PetscInt ovtrf = ovtlf + 1;
5837           const PetscInt ovtrb = ovtrf + 1;
5838           const PetscInt ovtlb = ovtrb + 1;
5839           PetscInt       o, n;
5840 
5841           /* Bottom Slice */
5842           /*   bottom */
5843           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
5844           for (o = oetf - 1; o >= oebf; --o)
5845             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5846           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
5847           /*   middle */
5848           for (i = 0; i < k - 1; ++i) {
5849             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
5850             for (n = 0; n < k - 1; ++n) {
5851               o = ofb + n * (k - 1) + i;
5852               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5853             }
5854             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
5855           }
5856           /*   top */
5857           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
5858           for (o = oebb; o < oebr; ++o)
5859             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5860           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
5861 
5862           /* Middle Slice */
5863           for (j = 0; j < k - 1; ++j) {
5864             /*   bottom */
5865             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
5866             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
5867               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5868             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
5869             /*   middle */
5870             for (i = 0; i < k - 1; ++i) {
5871               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
5872               for (n = 0; n < k - 1; ++n)
5873                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
5874               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
5875             }
5876             /*   top */
5877             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
5878             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
5879               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5880             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
5881           }
5882 
5883           /* Top Slice */
5884           /*   bottom */
5885           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
5886           for (o = oetf; o < oetr; ++o)
5887             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5888           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
5889           /*   middle */
5890           for (i = 0; i < k - 1; ++i) {
5891             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
5892             for (n = 0; n < k - 1; ++n)
5893               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
5894             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
5895           }
5896           /*   top */
5897           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
5898           for (o = oetl - 1; o >= oetb; --o)
5899             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5900           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
5901 
5902           foffset = offset;
5903         }
5904         break;
5905       default:
5906         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
5907       }
5908     }
5909     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
5910     /* Check permutation */
5911     {
5912       PetscInt *check;
5913 
5914       PetscCall(PetscMalloc1(size, &check));
5915       for (i = 0; i < size; ++i) {
5916         check[i] = -1;
5917         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
5918       }
5919       for (i = 0; i < size; ++i) check[perm[i]] = i;
5920       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
5921       PetscCall(PetscFree(check));
5922     }
5923     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
5924     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
5925       PetscInt *loc_perm;
5926       PetscCall(PetscMalloc1(size * 2, &loc_perm));
5927       for (PetscInt i = 0; i < size; i++) {
5928         loc_perm[i]        = perm[i];
5929         loc_perm[size + i] = size + perm[i];
5930       }
5931       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
5932     }
5933   }
5934   PetscFunctionReturn(PETSC_SUCCESS);
5935 }
5936 
5937 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
5938 {
5939   PetscDS  prob;
5940   PetscInt depth, Nf, h;
5941   DMLabel  label;
5942 
5943   PetscFunctionBeginHot;
5944   PetscCall(DMGetDS(dm, &prob));
5945   Nf      = prob->Nf;
5946   label   = dm->depthLabel;
5947   *dspace = NULL;
5948   if (field < Nf) {
5949     PetscObject disc = prob->disc[field];
5950 
5951     if (disc->classid == PETSCFE_CLASSID) {
5952       PetscDualSpace dsp;
5953 
5954       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
5955       PetscCall(DMLabelGetNumValues(label, &depth));
5956       PetscCall(DMLabelGetValue(label, point, &h));
5957       h = depth - 1 - h;
5958       if (h) {
5959         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
5960       } else {
5961         *dspace = dsp;
5962       }
5963     }
5964   }
5965   PetscFunctionReturn(PETSC_SUCCESS);
5966 }
5967 
5968 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5969 {
5970   PetscScalar       *array;
5971   const PetscScalar *vArray;
5972   const PetscInt    *cone, *coneO;
5973   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
5974 
5975   PetscFunctionBeginHot;
5976   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5977   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
5978   PetscCall(DMPlexGetCone(dm, point, &cone));
5979   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
5980   if (!values || !*values) {
5981     if ((point >= pStart) && (point < pEnd)) {
5982       PetscInt dof;
5983 
5984       PetscCall(PetscSectionGetDof(section, point, &dof));
5985       size += dof;
5986     }
5987     for (p = 0; p < numPoints; ++p) {
5988       const PetscInt cp = cone[p];
5989       PetscInt       dof;
5990 
5991       if ((cp < pStart) || (cp >= pEnd)) continue;
5992       PetscCall(PetscSectionGetDof(section, cp, &dof));
5993       size += dof;
5994     }
5995     if (!values) {
5996       if (csize) *csize = size;
5997       PetscFunctionReturn(PETSC_SUCCESS);
5998     }
5999     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
6000   } else {
6001     array = *values;
6002   }
6003   size = 0;
6004   PetscCall(VecGetArrayRead(v, &vArray));
6005   if ((point >= pStart) && (point < pEnd)) {
6006     PetscInt           dof, off, d;
6007     const PetscScalar *varr;
6008 
6009     PetscCall(PetscSectionGetDof(section, point, &dof));
6010     PetscCall(PetscSectionGetOffset(section, point, &off));
6011     varr = &vArray[off];
6012     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6013     size += dof;
6014   }
6015   for (p = 0; p < numPoints; ++p) {
6016     const PetscInt     cp = cone[p];
6017     PetscInt           o  = coneO[p];
6018     PetscInt           dof, off, d;
6019     const PetscScalar *varr;
6020 
6021     if ((cp < pStart) || (cp >= pEnd)) continue;
6022     PetscCall(PetscSectionGetDof(section, cp, &dof));
6023     PetscCall(PetscSectionGetOffset(section, cp, &off));
6024     varr = &vArray[off];
6025     if (o >= 0) {
6026       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6027     } else {
6028       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
6029     }
6030     size += dof;
6031   }
6032   PetscCall(VecRestoreArrayRead(v, &vArray));
6033   if (!*values) {
6034     if (csize) *csize = size;
6035     *values = array;
6036   } else {
6037     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6038     *csize = size;
6039   }
6040   PetscFunctionReturn(PETSC_SUCCESS);
6041 }
6042 
6043 /* Compress out points not in the section */
6044 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
6045 {
6046   const PetscInt np = *numPoints;
6047   PetscInt       pStart, pEnd, p, q;
6048 
6049   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6050   for (p = 0, q = 0; p < np; ++p) {
6051     const PetscInt r = points[p * 2];
6052     if ((r >= pStart) && (r < pEnd)) {
6053       points[q * 2]     = r;
6054       points[q * 2 + 1] = points[p * 2 + 1];
6055       ++q;
6056     }
6057   }
6058   *numPoints = q;
6059   return PETSC_SUCCESS;
6060 }
6061 
6062 /* Compressed closure does not apply closure permutation */
6063 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt ornt, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6064 {
6065   const PetscInt *cla = NULL;
6066   PetscInt        np, *pts = NULL;
6067 
6068   PetscFunctionBeginHot;
6069   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
6070   if (!ornt && *clPoints) {
6071     PetscInt dof, off;
6072 
6073     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
6074     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
6075     PetscCall(ISGetIndices(*clPoints, &cla));
6076     np  = dof / 2;
6077     pts = (PetscInt *)&cla[off];
6078   } else {
6079     PetscCall(DMPlexGetTransitiveClosure_Internal(dm, point, ornt, PETSC_TRUE, &np, &pts));
6080     PetscCall(CompressPoints_Private(section, &np, pts));
6081   }
6082   *numPoints = np;
6083   *points    = pts;
6084   *clp       = cla;
6085   PetscFunctionReturn(PETSC_SUCCESS);
6086 }
6087 
6088 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6089 {
6090   PetscFunctionBeginHot;
6091   if (!*clPoints) {
6092     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
6093   } else {
6094     PetscCall(ISRestoreIndices(*clPoints, clp));
6095   }
6096   *numPoints = 0;
6097   *points    = NULL;
6098   *clSec     = NULL;
6099   *clPoints  = NULL;
6100   *clp       = NULL;
6101   PetscFunctionReturn(PETSC_SUCCESS);
6102 }
6103 
6104 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
6105 {
6106   PetscInt            offset = 0, p;
6107   const PetscInt    **perms  = NULL;
6108   const PetscScalar **flips  = NULL;
6109 
6110   PetscFunctionBeginHot;
6111   *size = 0;
6112   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6113   for (p = 0; p < numPoints; p++) {
6114     const PetscInt     point = points[2 * p];
6115     const PetscInt    *perm  = perms ? perms[p] : NULL;
6116     const PetscScalar *flip  = flips ? flips[p] : NULL;
6117     PetscInt           dof, off, d;
6118     const PetscScalar *varr;
6119 
6120     PetscCall(PetscSectionGetDof(section, point, &dof));
6121     PetscCall(PetscSectionGetOffset(section, point, &off));
6122     varr = &vArray[off];
6123     if (clperm) {
6124       if (perm) {
6125         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
6126       } else {
6127         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
6128       }
6129       if (flip) {
6130         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
6131       }
6132     } else {
6133       if (perm) {
6134         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
6135       } else {
6136         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
6137       }
6138       if (flip) {
6139         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
6140       }
6141     }
6142     offset += dof;
6143   }
6144   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6145   *size = offset;
6146   PetscFunctionReturn(PETSC_SUCCESS);
6147 }
6148 
6149 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[])
6150 {
6151   PetscInt offset = 0, f;
6152 
6153   PetscFunctionBeginHot;
6154   *size = 0;
6155   for (f = 0; f < numFields; ++f) {
6156     PetscInt            p;
6157     const PetscInt    **perms = NULL;
6158     const PetscScalar **flips = NULL;
6159 
6160     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6161     for (p = 0; p < numPoints; p++) {
6162       const PetscInt     point = points[2 * p];
6163       PetscInt           fdof, foff, b;
6164       const PetscScalar *varr;
6165       const PetscInt    *perm = perms ? perms[p] : NULL;
6166       const PetscScalar *flip = flips ? flips[p] : NULL;
6167 
6168       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6169       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6170       varr = &vArray[foff];
6171       if (clperm) {
6172         if (perm) {
6173           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
6174         } else {
6175           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
6176         }
6177         if (flip) {
6178           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
6179         }
6180       } else {
6181         if (perm) {
6182           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
6183         } else {
6184           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
6185         }
6186         if (flip) {
6187           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
6188         }
6189       }
6190       offset += fdof;
6191     }
6192     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6193   }
6194   *size = offset;
6195   PetscFunctionReturn(PETSC_SUCCESS);
6196 }
6197 
6198 PetscErrorCode DMPlexVecGetOrientedClosure_Internal(DM dm, PetscSection section, PetscBool useClPerm, Vec v, PetscInt point, PetscInt ornt, PetscInt *csize, PetscScalar *values[])
6199 {
6200   PetscSection    clSection;
6201   IS              clPoints;
6202   PetscInt       *points = NULL;
6203   const PetscInt *clp, *perm = NULL;
6204   PetscInt        depth, numFields, numPoints, asize;
6205 
6206   PetscFunctionBeginHot;
6207   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6208   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6209   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6210   PetscValidHeaderSpecific(v, VEC_CLASSID, 4);
6211   PetscCall(DMPlexGetDepth(dm, &depth));
6212   PetscCall(PetscSectionGetNumFields(section, &numFields));
6213   if (depth == 1 && numFields < 2) {
6214     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6215     PetscFunctionReturn(PETSC_SUCCESS);
6216   }
6217   /* Get points */
6218   PetscCall(DMPlexGetCompressedClosure(dm, section, point, ornt, &numPoints, &points, &clSection, &clPoints, &clp));
6219   /* Get sizes */
6220   asize = 0;
6221   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6222     PetscInt dof;
6223     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6224     asize += dof;
6225   }
6226   if (values) {
6227     const PetscScalar *vArray;
6228     PetscInt           size;
6229 
6230     if (*values) {
6231       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);
6232     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6233     if (useClPerm) PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6234     PetscCall(VecGetArrayRead(v, &vArray));
6235     /* Get values */
6236     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6237     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6238     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6239     /* Cleanup array */
6240     PetscCall(VecRestoreArrayRead(v, &vArray));
6241   }
6242   if (csize) *csize = asize;
6243   /* Cleanup points */
6244   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6245   PetscFunctionReturn(PETSC_SUCCESS);
6246 }
6247 
6248 /*@C
6249   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
6250 
6251   Not collective
6252 
6253   Input Parameters:
6254 + dm      - The `DM`
6255 . section - The section describing the layout in `v`, or `NULL` to use the default section
6256 . v       - The local vector
6257 - point   - The point in the `DM`
6258 
6259   Input/Output Parameters:
6260 + csize  - The size of the input values array, or `NULL`; on output the number of values in the closure
6261 - values - An array to use for the values, or `NULL` to have it allocated automatically;
6262            if the user provided `NULL`, it is a borrowed array and should not be freed
6263 
6264   Level: intermediate
6265 
6266   Notes:
6267   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6268   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
6269   assembly function, and a user may already have allocated storage for this operation.
6270 
6271   A typical use could be
6272 .vb
6273    values = NULL;
6274    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6275    for (cl = 0; cl < clSize; ++cl) {
6276      <Compute on closure>
6277    }
6278    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
6279 .ve
6280   or
6281 .vb
6282    PetscMalloc1(clMaxSize, &values);
6283    for (p = pStart; p < pEnd; ++p) {
6284      clSize = clMaxSize;
6285      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6286      for (cl = 0; cl < clSize; ++cl) {
6287        <Compute on closure>
6288      }
6289    }
6290    PetscFree(values);
6291 .ve
6292 
6293   Fortran Notes:
6294   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6295 
6296 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6297 @*/
6298 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6299 {
6300   PetscFunctionBeginHot;
6301   PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, section, PETSC_TRUE, v, point, 0, csize, values));
6302   PetscFunctionReturn(PETSC_SUCCESS);
6303 }
6304 
6305 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6306 {
6307   DMLabel            depthLabel;
6308   PetscSection       clSection;
6309   IS                 clPoints;
6310   PetscScalar       *array;
6311   const PetscScalar *vArray;
6312   PetscInt          *points = NULL;
6313   const PetscInt    *clp, *perm = NULL;
6314   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6315 
6316   PetscFunctionBeginHot;
6317   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6318   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6319   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6320   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6321   PetscCall(DMPlexGetDepth(dm, &mdepth));
6322   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6323   PetscCall(PetscSectionGetNumFields(section, &numFields));
6324   if (mdepth == 1 && numFields < 2) {
6325     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6326     PetscFunctionReturn(PETSC_SUCCESS);
6327   }
6328   /* Get points */
6329   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6330   for (clsize = 0, p = 0; p < Np; p++) {
6331     PetscInt dof;
6332     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6333     clsize += dof;
6334   }
6335   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6336   /* Filter points */
6337   for (p = 0; p < numPoints * 2; p += 2) {
6338     PetscInt dep;
6339 
6340     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6341     if (dep != depth) continue;
6342     points[Np * 2 + 0] = points[p];
6343     points[Np * 2 + 1] = points[p + 1];
6344     ++Np;
6345   }
6346   /* Get array */
6347   if (!values || !*values) {
6348     PetscInt asize = 0, dof;
6349 
6350     for (p = 0; p < Np * 2; p += 2) {
6351       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6352       asize += dof;
6353     }
6354     if (!values) {
6355       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6356       if (csize) *csize = asize;
6357       PetscFunctionReturn(PETSC_SUCCESS);
6358     }
6359     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6360   } else {
6361     array = *values;
6362   }
6363   PetscCall(VecGetArrayRead(v, &vArray));
6364   /* Get values */
6365   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6366   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6367   /* Cleanup points */
6368   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6369   /* Cleanup array */
6370   PetscCall(VecRestoreArrayRead(v, &vArray));
6371   if (!*values) {
6372     if (csize) *csize = size;
6373     *values = array;
6374   } else {
6375     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6376     *csize = size;
6377   }
6378   PetscFunctionReturn(PETSC_SUCCESS);
6379 }
6380 
6381 /*@C
6382   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point'
6383 
6384   Not collective
6385 
6386   Input Parameters:
6387 + dm      - The `DM`
6388 . section - The section describing the layout in `v`, or `NULL` to use the default section
6389 . v       - The local vector
6390 . point   - The point in the `DM`
6391 . csize   - The number of values in the closure, or `NULL`
6392 - values  - The array of values, which is a borrowed array and should not be freed
6393 
6394   Level: intermediate
6395 
6396   Note:
6397   The array values are discarded and not copied back into `v`. In order to copy values back to `v`, use `DMPlexVecSetClosure()`
6398 
6399   Fortran Notes:
6400   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6401 
6402 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6403 @*/
6404 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6405 {
6406   PetscInt size = 0;
6407 
6408   PetscFunctionBegin;
6409   /* Should work without recalculating size */
6410   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6411   *values = NULL;
6412   PetscFunctionReturn(PETSC_SUCCESS);
6413 }
6414 
6415 static inline void add(PetscScalar *x, PetscScalar y)
6416 {
6417   *x += y;
6418 }
6419 static inline void insert(PetscScalar *x, PetscScalar y)
6420 {
6421   *x = y;
6422 }
6423 
6424 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[])
6425 {
6426   PetscInt        cdof;  /* The number of constraints on this point */
6427   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6428   PetscScalar    *a;
6429   PetscInt        off, cind = 0, k;
6430 
6431   PetscFunctionBegin;
6432   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6433   PetscCall(PetscSectionGetOffset(section, point, &off));
6434   a = &array[off];
6435   if (!cdof || setBC) {
6436     if (clperm) {
6437       if (perm) {
6438         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6439       } else {
6440         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6441       }
6442     } else {
6443       if (perm) {
6444         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6445       } else {
6446         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6447       }
6448     }
6449   } else {
6450     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6451     if (clperm) {
6452       if (perm) {
6453         for (k = 0; k < dof; ++k) {
6454           if ((cind < cdof) && (k == cdofs[cind])) {
6455             ++cind;
6456             continue;
6457           }
6458           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6459         }
6460       } else {
6461         for (k = 0; k < dof; ++k) {
6462           if ((cind < cdof) && (k == cdofs[cind])) {
6463             ++cind;
6464             continue;
6465           }
6466           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6467         }
6468       }
6469     } else {
6470       if (perm) {
6471         for (k = 0; k < dof; ++k) {
6472           if ((cind < cdof) && (k == cdofs[cind])) {
6473             ++cind;
6474             continue;
6475           }
6476           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6477         }
6478       } else {
6479         for (k = 0; k < dof; ++k) {
6480           if ((cind < cdof) && (k == cdofs[cind])) {
6481             ++cind;
6482             continue;
6483           }
6484           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6485         }
6486       }
6487     }
6488   }
6489   PetscFunctionReturn(PETSC_SUCCESS);
6490 }
6491 
6492 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[])
6493 {
6494   PetscInt        cdof;  /* The number of constraints on this point */
6495   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6496   PetscScalar    *a;
6497   PetscInt        off, cind = 0, k;
6498 
6499   PetscFunctionBegin;
6500   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6501   PetscCall(PetscSectionGetOffset(section, point, &off));
6502   a = &array[off];
6503   if (cdof) {
6504     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6505     if (clperm) {
6506       if (perm) {
6507         for (k = 0; k < dof; ++k) {
6508           if ((cind < cdof) && (k == cdofs[cind])) {
6509             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6510             cind++;
6511           }
6512         }
6513       } else {
6514         for (k = 0; k < dof; ++k) {
6515           if ((cind < cdof) && (k == cdofs[cind])) {
6516             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6517             cind++;
6518           }
6519         }
6520       }
6521     } else {
6522       if (perm) {
6523         for (k = 0; k < dof; ++k) {
6524           if ((cind < cdof) && (k == cdofs[cind])) {
6525             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6526             cind++;
6527           }
6528         }
6529       } else {
6530         for (k = 0; k < dof; ++k) {
6531           if ((cind < cdof) && (k == cdofs[cind])) {
6532             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6533             cind++;
6534           }
6535         }
6536       }
6537     }
6538   }
6539   PetscFunctionReturn(PETSC_SUCCESS);
6540 }
6541 
6542 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[])
6543 {
6544   PetscScalar    *a;
6545   PetscInt        fdof, foff, fcdof, foffset = *offset;
6546   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6547   PetscInt        cind = 0, b;
6548 
6549   PetscFunctionBegin;
6550   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6551   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6552   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6553   a = &array[foff];
6554   if (!fcdof || setBC) {
6555     if (clperm) {
6556       if (perm) {
6557         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6558       } else {
6559         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6560       }
6561     } else {
6562       if (perm) {
6563         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6564       } else {
6565         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6566       }
6567     }
6568   } else {
6569     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6570     if (clperm) {
6571       if (perm) {
6572         for (b = 0; b < fdof; b++) {
6573           if ((cind < fcdof) && (b == fcdofs[cind])) {
6574             ++cind;
6575             continue;
6576           }
6577           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6578         }
6579       } else {
6580         for (b = 0; b < fdof; b++) {
6581           if ((cind < fcdof) && (b == fcdofs[cind])) {
6582             ++cind;
6583             continue;
6584           }
6585           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6586         }
6587       }
6588     } else {
6589       if (perm) {
6590         for (b = 0; b < fdof; b++) {
6591           if ((cind < fcdof) && (b == fcdofs[cind])) {
6592             ++cind;
6593             continue;
6594           }
6595           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6596         }
6597       } else {
6598         for (b = 0; b < fdof; b++) {
6599           if ((cind < fcdof) && (b == fcdofs[cind])) {
6600             ++cind;
6601             continue;
6602           }
6603           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6604         }
6605       }
6606     }
6607   }
6608   *offset += fdof;
6609   PetscFunctionReturn(PETSC_SUCCESS);
6610 }
6611 
6612 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[])
6613 {
6614   PetscScalar    *a;
6615   PetscInt        fdof, foff, fcdof, foffset = *offset;
6616   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6617   PetscInt        Nc, cind = 0, ncind = 0, b;
6618   PetscBool       ncSet, fcSet;
6619 
6620   PetscFunctionBegin;
6621   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6622   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6623   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6624   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6625   a = &array[foff];
6626   if (fcdof) {
6627     /* We just override fcdof and fcdofs with Ncc and comps */
6628     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6629     if (clperm) {
6630       if (perm) {
6631         if (comps) {
6632           for (b = 0; b < fdof; b++) {
6633             ncSet = fcSet = PETSC_FALSE;
6634             if (b % Nc == comps[ncind]) {
6635               ncind = (ncind + 1) % Ncc;
6636               ncSet = PETSC_TRUE;
6637             }
6638             if ((cind < fcdof) && (b == fcdofs[cind])) {
6639               ++cind;
6640               fcSet = PETSC_TRUE;
6641             }
6642             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6643           }
6644         } else {
6645           for (b = 0; b < fdof; b++) {
6646             if ((cind < fcdof) && (b == fcdofs[cind])) {
6647               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6648               ++cind;
6649             }
6650           }
6651         }
6652       } else {
6653         if (comps) {
6654           for (b = 0; b < fdof; b++) {
6655             ncSet = fcSet = PETSC_FALSE;
6656             if (b % Nc == comps[ncind]) {
6657               ncind = (ncind + 1) % Ncc;
6658               ncSet = PETSC_TRUE;
6659             }
6660             if ((cind < fcdof) && (b == fcdofs[cind])) {
6661               ++cind;
6662               fcSet = PETSC_TRUE;
6663             }
6664             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6665           }
6666         } else {
6667           for (b = 0; b < fdof; b++) {
6668             if ((cind < fcdof) && (b == fcdofs[cind])) {
6669               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6670               ++cind;
6671             }
6672           }
6673         }
6674       }
6675     } else {
6676       if (perm) {
6677         if (comps) {
6678           for (b = 0; b < fdof; b++) {
6679             ncSet = fcSet = PETSC_FALSE;
6680             if (b % Nc == comps[ncind]) {
6681               ncind = (ncind + 1) % Ncc;
6682               ncSet = PETSC_TRUE;
6683             }
6684             if ((cind < fcdof) && (b == fcdofs[cind])) {
6685               ++cind;
6686               fcSet = PETSC_TRUE;
6687             }
6688             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6689           }
6690         } else {
6691           for (b = 0; b < fdof; b++) {
6692             if ((cind < fcdof) && (b == fcdofs[cind])) {
6693               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6694               ++cind;
6695             }
6696           }
6697         }
6698       } else {
6699         if (comps) {
6700           for (b = 0; b < fdof; b++) {
6701             ncSet = fcSet = PETSC_FALSE;
6702             if (b % Nc == comps[ncind]) {
6703               ncind = (ncind + 1) % Ncc;
6704               ncSet = PETSC_TRUE;
6705             }
6706             if ((cind < fcdof) && (b == fcdofs[cind])) {
6707               ++cind;
6708               fcSet = PETSC_TRUE;
6709             }
6710             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6711           }
6712         } else {
6713           for (b = 0; b < fdof; b++) {
6714             if ((cind < fcdof) && (b == fcdofs[cind])) {
6715               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6716               ++cind;
6717             }
6718           }
6719         }
6720       }
6721     }
6722   }
6723   *offset += fdof;
6724   PetscFunctionReturn(PETSC_SUCCESS);
6725 }
6726 
6727 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6728 {
6729   PetscScalar    *array;
6730   const PetscInt *cone, *coneO;
6731   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6732 
6733   PetscFunctionBeginHot;
6734   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6735   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6736   PetscCall(DMPlexGetCone(dm, point, &cone));
6737   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6738   PetscCall(VecGetArray(v, &array));
6739   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6740     const PetscInt cp = !p ? point : cone[p - 1];
6741     const PetscInt o  = !p ? 0 : coneO[p - 1];
6742 
6743     if ((cp < pStart) || (cp >= pEnd)) {
6744       dof = 0;
6745       continue;
6746     }
6747     PetscCall(PetscSectionGetDof(section, cp, &dof));
6748     /* ADD_VALUES */
6749     {
6750       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6751       PetscScalar    *a;
6752       PetscInt        cdof, coff, cind = 0, k;
6753 
6754       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
6755       PetscCall(PetscSectionGetOffset(section, cp, &coff));
6756       a = &array[coff];
6757       if (!cdof) {
6758         if (o >= 0) {
6759           for (k = 0; k < dof; ++k) a[k] += values[off + k];
6760         } else {
6761           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
6762         }
6763       } else {
6764         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
6765         if (o >= 0) {
6766           for (k = 0; k < dof; ++k) {
6767             if ((cind < cdof) && (k == cdofs[cind])) {
6768               ++cind;
6769               continue;
6770             }
6771             a[k] += values[off + k];
6772           }
6773         } else {
6774           for (k = 0; k < dof; ++k) {
6775             if ((cind < cdof) && (k == cdofs[cind])) {
6776               ++cind;
6777               continue;
6778             }
6779             a[k] += values[off + dof - k - 1];
6780           }
6781         }
6782       }
6783     }
6784   }
6785   PetscCall(VecRestoreArray(v, &array));
6786   PetscFunctionReturn(PETSC_SUCCESS);
6787 }
6788 
6789 /*@C
6790   DMPlexVecSetClosure - Set an array of the values on the closure of `point`
6791 
6792   Not collective
6793 
6794   Input Parameters:
6795 + dm      - The `DM`
6796 . section - The section describing the layout in `v`, or `NULL` to use the default section
6797 . v       - The local vector
6798 . point   - The point in the `DM`
6799 . values  - The array of values
6800 - mode    - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
6801          where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
6802 
6803   Level: intermediate
6804 
6805 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
6806 @*/
6807 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6808 {
6809   PetscSection    clSection;
6810   IS              clPoints;
6811   PetscScalar    *array;
6812   PetscInt       *points = NULL;
6813   const PetscInt *clp, *clperm = NULL;
6814   PetscInt        depth, numFields, numPoints, p, clsize;
6815 
6816   PetscFunctionBeginHot;
6817   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6818   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6819   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6820   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6821   PetscCall(DMPlexGetDepth(dm, &depth));
6822   PetscCall(PetscSectionGetNumFields(section, &numFields));
6823   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
6824     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
6825     PetscFunctionReturn(PETSC_SUCCESS);
6826   }
6827   /* Get points */
6828   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6829   for (clsize = 0, p = 0; p < numPoints; p++) {
6830     PetscInt dof;
6831     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6832     clsize += dof;
6833   }
6834   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
6835   /* Get array */
6836   PetscCall(VecGetArray(v, &array));
6837   /* Get values */
6838   if (numFields > 0) {
6839     PetscInt offset = 0, f;
6840     for (f = 0; f < numFields; ++f) {
6841       const PetscInt    **perms = NULL;
6842       const PetscScalar **flips = NULL;
6843 
6844       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6845       switch (mode) {
6846       case INSERT_VALUES:
6847         for (p = 0; p < numPoints; p++) {
6848           const PetscInt     point = points[2 * p];
6849           const PetscInt    *perm  = perms ? perms[p] : NULL;
6850           const PetscScalar *flip  = flips ? flips[p] : NULL;
6851           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array));
6852         }
6853         break;
6854       case INSERT_ALL_VALUES:
6855         for (p = 0; p < numPoints; p++) {
6856           const PetscInt     point = points[2 * p];
6857           const PetscInt    *perm  = perms ? perms[p] : NULL;
6858           const PetscScalar *flip  = flips ? flips[p] : NULL;
6859           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array));
6860         }
6861         break;
6862       case INSERT_BC_VALUES:
6863         for (p = 0; p < numPoints; p++) {
6864           const PetscInt     point = points[2 * p];
6865           const PetscInt    *perm  = perms ? perms[p] : NULL;
6866           const PetscScalar *flip  = flips ? flips[p] : NULL;
6867           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array));
6868         }
6869         break;
6870       case ADD_VALUES:
6871         for (p = 0; p < numPoints; p++) {
6872           const PetscInt     point = points[2 * p];
6873           const PetscInt    *perm  = perms ? perms[p] : NULL;
6874           const PetscScalar *flip  = flips ? flips[p] : NULL;
6875           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array));
6876         }
6877         break;
6878       case ADD_ALL_VALUES:
6879         for (p = 0; p < numPoints; p++) {
6880           const PetscInt     point = points[2 * p];
6881           const PetscInt    *perm  = perms ? perms[p] : NULL;
6882           const PetscScalar *flip  = flips ? flips[p] : NULL;
6883           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array));
6884         }
6885         break;
6886       case ADD_BC_VALUES:
6887         for (p = 0; p < numPoints; p++) {
6888           const PetscInt     point = points[2 * p];
6889           const PetscInt    *perm  = perms ? perms[p] : NULL;
6890           const PetscScalar *flip  = flips ? flips[p] : NULL;
6891           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array));
6892         }
6893         break;
6894       default:
6895         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6896       }
6897       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6898     }
6899   } else {
6900     PetscInt            dof, off;
6901     const PetscInt    **perms = NULL;
6902     const PetscScalar **flips = NULL;
6903 
6904     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6905     switch (mode) {
6906     case INSERT_VALUES:
6907       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6908         const PetscInt     point = points[2 * p];
6909         const PetscInt    *perm  = perms ? perms[p] : NULL;
6910         const PetscScalar *flip  = flips ? flips[p] : NULL;
6911         PetscCall(PetscSectionGetDof(section, point, &dof));
6912         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array));
6913       }
6914       break;
6915     case INSERT_ALL_VALUES:
6916       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6917         const PetscInt     point = points[2 * p];
6918         const PetscInt    *perm  = perms ? perms[p] : NULL;
6919         const PetscScalar *flip  = flips ? flips[p] : NULL;
6920         PetscCall(PetscSectionGetDof(section, point, &dof));
6921         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array));
6922       }
6923       break;
6924     case INSERT_BC_VALUES:
6925       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6926         const PetscInt     point = points[2 * p];
6927         const PetscInt    *perm  = perms ? perms[p] : NULL;
6928         const PetscScalar *flip  = flips ? flips[p] : NULL;
6929         PetscCall(PetscSectionGetDof(section, point, &dof));
6930         PetscCall(updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array));
6931       }
6932       break;
6933     case ADD_VALUES:
6934       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6935         const PetscInt     point = points[2 * p];
6936         const PetscInt    *perm  = perms ? perms[p] : NULL;
6937         const PetscScalar *flip  = flips ? flips[p] : NULL;
6938         PetscCall(PetscSectionGetDof(section, point, &dof));
6939         PetscCall(updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array));
6940       }
6941       break;
6942     case ADD_ALL_VALUES:
6943       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6944         const PetscInt     point = points[2 * p];
6945         const PetscInt    *perm  = perms ? perms[p] : NULL;
6946         const PetscScalar *flip  = flips ? flips[p] : NULL;
6947         PetscCall(PetscSectionGetDof(section, point, &dof));
6948         PetscCall(updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array));
6949       }
6950       break;
6951     case ADD_BC_VALUES:
6952       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6953         const PetscInt     point = points[2 * p];
6954         const PetscInt    *perm  = perms ? perms[p] : NULL;
6955         const PetscScalar *flip  = flips ? flips[p] : NULL;
6956         PetscCall(PetscSectionGetDof(section, point, &dof));
6957         PetscCall(updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array));
6958       }
6959       break;
6960     default:
6961       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6962     }
6963     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6964   }
6965   /* Cleanup points */
6966   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6967   /* Cleanup array */
6968   PetscCall(VecRestoreArray(v, &array));
6969   PetscFunctionReturn(PETSC_SUCCESS);
6970 }
6971 
6972 /* Check whether the given point is in the label. If not, update the offset to skip this point */
6973 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
6974 {
6975   PetscFunctionBegin;
6976   *contains = PETSC_TRUE;
6977   if (label) {
6978     PetscInt fdof;
6979 
6980     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
6981     if (!*contains) {
6982       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6983       *offset += fdof;
6984       PetscFunctionReturn(PETSC_SUCCESS);
6985     }
6986   }
6987   PetscFunctionReturn(PETSC_SUCCESS);
6988 }
6989 
6990 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
6991 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)
6992 {
6993   PetscSection    clSection;
6994   IS              clPoints;
6995   PetscScalar    *array;
6996   PetscInt       *points = NULL;
6997   const PetscInt *clp;
6998   PetscInt        numFields, numPoints, p;
6999   PetscInt        offset = 0, f;
7000 
7001   PetscFunctionBeginHot;
7002   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7003   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7004   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7005   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7006   PetscCall(PetscSectionGetNumFields(section, &numFields));
7007   /* Get points */
7008   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7009   /* Get array */
7010   PetscCall(VecGetArray(v, &array));
7011   /* Get values */
7012   for (f = 0; f < numFields; ++f) {
7013     const PetscInt    **perms = NULL;
7014     const PetscScalar **flips = NULL;
7015     PetscBool           contains;
7016 
7017     if (!fieldActive[f]) {
7018       for (p = 0; p < numPoints * 2; p += 2) {
7019         PetscInt fdof;
7020         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7021         offset += fdof;
7022       }
7023       continue;
7024     }
7025     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7026     switch (mode) {
7027     case INSERT_VALUES:
7028       for (p = 0; p < numPoints; p++) {
7029         const PetscInt     point = points[2 * p];
7030         const PetscInt    *perm  = perms ? perms[p] : NULL;
7031         const PetscScalar *flip  = flips ? flips[p] : NULL;
7032         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7033         if (!contains) continue;
7034         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
7035       }
7036       break;
7037     case INSERT_ALL_VALUES:
7038       for (p = 0; p < numPoints; p++) {
7039         const PetscInt     point = points[2 * p];
7040         const PetscInt    *perm  = perms ? perms[p] : NULL;
7041         const PetscScalar *flip  = flips ? flips[p] : NULL;
7042         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7043         if (!contains) continue;
7044         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
7045       }
7046       break;
7047     case INSERT_BC_VALUES:
7048       for (p = 0; p < numPoints; p++) {
7049         const PetscInt     point = points[2 * p];
7050         const PetscInt    *perm  = perms ? perms[p] : NULL;
7051         const PetscScalar *flip  = flips ? flips[p] : NULL;
7052         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7053         if (!contains) continue;
7054         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
7055       }
7056       break;
7057     case ADD_VALUES:
7058       for (p = 0; p < numPoints; p++) {
7059         const PetscInt     point = points[2 * p];
7060         const PetscInt    *perm  = perms ? perms[p] : NULL;
7061         const PetscScalar *flip  = flips ? flips[p] : NULL;
7062         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7063         if (!contains) continue;
7064         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
7065       }
7066       break;
7067     case ADD_ALL_VALUES:
7068       for (p = 0; p < numPoints; p++) {
7069         const PetscInt     point = points[2 * p];
7070         const PetscInt    *perm  = perms ? perms[p] : NULL;
7071         const PetscScalar *flip  = flips ? flips[p] : NULL;
7072         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7073         if (!contains) continue;
7074         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
7075       }
7076       break;
7077     default:
7078       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7079     }
7080     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7081   }
7082   /* Cleanup points */
7083   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7084   /* Cleanup array */
7085   PetscCall(VecRestoreArray(v, &array));
7086   PetscFunctionReturn(PETSC_SUCCESS);
7087 }
7088 
7089 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
7090 {
7091   PetscMPIInt rank;
7092   PetscInt    i, j;
7093 
7094   PetscFunctionBegin;
7095   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7096   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
7097   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
7098   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
7099   numCIndices = numCIndices ? numCIndices : numRIndices;
7100   if (!values) PetscFunctionReturn(PETSC_SUCCESS);
7101   for (i = 0; i < numRIndices; i++) {
7102     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
7103     for (j = 0; j < numCIndices; j++) {
7104 #if defined(PETSC_USE_COMPLEX)
7105       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
7106 #else
7107       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
7108 #endif
7109     }
7110     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
7111   }
7112   PetscFunctionReturn(PETSC_SUCCESS);
7113 }
7114 
7115 /*
7116   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
7117 
7118   Input Parameters:
7119 + section - The section for this data layout
7120 . islocal - Is the section (and thus indices being requested) local or global?
7121 . point   - The point contributing dofs with these indices
7122 . off     - The global offset of this point
7123 . loff    - The local offset of each field
7124 . setBC   - The flag determining whether to include indices of boundary values
7125 . perm    - A permutation of the dofs on this point, or NULL
7126 - indperm - A permutation of the entire indices array, or NULL
7127 
7128   Output Parameter:
7129 . indices - Indices for dofs on this point
7130 
7131   Level: developer
7132 
7133   Note: The indices could be local or global, depending on the value of 'off'.
7134 */
7135 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
7136 {
7137   PetscInt        dof;   /* The number of unknowns on this point */
7138   PetscInt        cdof;  /* The number of constraints on this point */
7139   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7140   PetscInt        cind = 0, k;
7141 
7142   PetscFunctionBegin;
7143   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7144   PetscCall(PetscSectionGetDof(section, point, &dof));
7145   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
7146   if (!cdof || setBC) {
7147     for (k = 0; k < dof; ++k) {
7148       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7149       const PetscInt ind    = indperm ? indperm[preind] : preind;
7150 
7151       indices[ind] = off + k;
7152     }
7153   } else {
7154     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
7155     for (k = 0; k < dof; ++k) {
7156       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7157       const PetscInt ind    = indperm ? indperm[preind] : preind;
7158 
7159       if ((cind < cdof) && (k == cdofs[cind])) {
7160         /* Insert check for returning constrained indices */
7161         indices[ind] = -(off + k + 1);
7162         ++cind;
7163       } else {
7164         indices[ind] = off + k - (islocal ? 0 : cind);
7165       }
7166     }
7167   }
7168   *loff += dof;
7169   PetscFunctionReturn(PETSC_SUCCESS);
7170 }
7171 
7172 /*
7173  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
7174 
7175  Input Parameters:
7176 + section - a section (global or local)
7177 - islocal - `PETSC_TRUE` if requesting local indices (i.e., section is local); `PETSC_FALSE` for global
7178 . point - point within section
7179 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
7180 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
7181 . setBC - identify constrained (boundary condition) points via involution.
7182 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
7183 . permsoff - offset
7184 - indperm - index permutation
7185 
7186  Output Parameter:
7187 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
7188 . indices - array to hold indices (as defined by section) of each dof associated with point
7189 
7190  Notes:
7191  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
7192  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
7193  in the local vector.
7194 
7195  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
7196  significant).  It is invalid to call with a global section and setBC=true.
7197 
7198  Developer Note:
7199  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
7200  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
7201  offset could be obtained from the section instead of passing it explicitly as we do now.
7202 
7203  Example:
7204  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
7205  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
7206  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
7207  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.
7208 
7209  Level: developer
7210 */
7211 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[])
7212 {
7213   PetscInt numFields, foff, f;
7214 
7215   PetscFunctionBegin;
7216   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7217   PetscCall(PetscSectionGetNumFields(section, &numFields));
7218   for (f = 0, foff = 0; f < numFields; ++f) {
7219     PetscInt        fdof, cfdof;
7220     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7221     PetscInt        cind = 0, b;
7222     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7223 
7224     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7225     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7226     if (!cfdof || setBC) {
7227       for (b = 0; b < fdof; ++b) {
7228         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7229         const PetscInt ind    = indperm ? indperm[preind] : preind;
7230 
7231         indices[ind] = off + foff + b;
7232       }
7233     } else {
7234       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7235       for (b = 0; b < fdof; ++b) {
7236         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7237         const PetscInt ind    = indperm ? indperm[preind] : preind;
7238 
7239         if ((cind < cfdof) && (b == fcdofs[cind])) {
7240           indices[ind] = -(off + foff + b + 1);
7241           ++cind;
7242         } else {
7243           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7244         }
7245       }
7246     }
7247     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7248     foffs[f] += fdof;
7249   }
7250   PetscFunctionReturn(PETSC_SUCCESS);
7251 }
7252 
7253 /*
7254   This version believes the globalSection offsets for each field, rather than just the point offset
7255 
7256  . foffs - The offset into 'indices' for each field, since it is segregated by field
7257 
7258  Notes:
7259  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7260  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7261 */
7262 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7263 {
7264   PetscInt numFields, foff, f;
7265 
7266   PetscFunctionBegin;
7267   PetscCall(PetscSectionGetNumFields(section, &numFields));
7268   for (f = 0; f < numFields; ++f) {
7269     PetscInt        fdof, cfdof;
7270     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7271     PetscInt        cind = 0, b;
7272     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7273 
7274     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7275     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7276     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7277     if (!cfdof) {
7278       for (b = 0; b < fdof; ++b) {
7279         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7280         const PetscInt ind    = indperm ? indperm[preind] : preind;
7281 
7282         indices[ind] = foff + b;
7283       }
7284     } else {
7285       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7286       for (b = 0; b < fdof; ++b) {
7287         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7288         const PetscInt ind    = indperm ? indperm[preind] : preind;
7289 
7290         if ((cind < cfdof) && (b == fcdofs[cind])) {
7291           indices[ind] = -(foff + b + 1);
7292           ++cind;
7293         } else {
7294           indices[ind] = foff + b - cind;
7295         }
7296       }
7297     }
7298     foffs[f] += fdof;
7299   }
7300   PetscFunctionReturn(PETSC_SUCCESS);
7301 }
7302 
7303 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)
7304 {
7305   Mat             cMat;
7306   PetscSection    aSec, cSec;
7307   IS              aIS;
7308   PetscInt        aStart = -1, aEnd = -1;
7309   const PetscInt *anchors;
7310   PetscInt        numFields, f, p, q, newP = 0;
7311   PetscInt        newNumPoints = 0, newNumIndices = 0;
7312   PetscInt       *newPoints, *indices, *newIndices;
7313   PetscInt        maxAnchor, maxDof;
7314   PetscInt        newOffsets[32];
7315   PetscInt       *pointMatOffsets[32];
7316   PetscInt       *newPointOffsets[32];
7317   PetscScalar    *pointMat[32];
7318   PetscScalar    *newValues      = NULL, *tmpValues;
7319   PetscBool       anyConstrained = PETSC_FALSE;
7320 
7321   PetscFunctionBegin;
7322   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7323   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7324   PetscCall(PetscSectionGetNumFields(section, &numFields));
7325 
7326   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7327   /* if there are point-to-point constraints */
7328   if (aSec) {
7329     PetscCall(PetscArrayzero(newOffsets, 32));
7330     PetscCall(ISGetIndices(aIS, &anchors));
7331     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7332     /* figure out how many points are going to be in the new element matrix
7333      * (we allow double counting, because it's all just going to be summed
7334      * into the global matrix anyway) */
7335     for (p = 0; p < 2 * numPoints; p += 2) {
7336       PetscInt b    = points[p];
7337       PetscInt bDof = 0, bSecDof;
7338 
7339       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7340       if (!bSecDof) continue;
7341       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7342       if (bDof) {
7343         /* this point is constrained */
7344         /* it is going to be replaced by its anchors */
7345         PetscInt bOff, q;
7346 
7347         anyConstrained = PETSC_TRUE;
7348         newNumPoints += bDof;
7349         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7350         for (q = 0; q < bDof; q++) {
7351           PetscInt a = anchors[bOff + q];
7352           PetscInt aDof;
7353 
7354           PetscCall(PetscSectionGetDof(section, a, &aDof));
7355           newNumIndices += aDof;
7356           for (f = 0; f < numFields; ++f) {
7357             PetscInt fDof;
7358 
7359             PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7360             newOffsets[f + 1] += fDof;
7361           }
7362         }
7363       } else {
7364         /* this point is not constrained */
7365         newNumPoints++;
7366         newNumIndices += bSecDof;
7367         for (f = 0; f < numFields; ++f) {
7368           PetscInt fDof;
7369 
7370           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7371           newOffsets[f + 1] += fDof;
7372         }
7373       }
7374     }
7375   }
7376   if (!anyConstrained) {
7377     if (outNumPoints) *outNumPoints = 0;
7378     if (outNumIndices) *outNumIndices = 0;
7379     if (outPoints) *outPoints = NULL;
7380     if (outValues) *outValues = NULL;
7381     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7382     PetscFunctionReturn(PETSC_SUCCESS);
7383   }
7384 
7385   if (outNumPoints) *outNumPoints = newNumPoints;
7386   if (outNumIndices) *outNumIndices = newNumIndices;
7387 
7388   for (f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7389 
7390   if (!outPoints && !outValues) {
7391     if (offsets) {
7392       for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7393     }
7394     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7395     PetscFunctionReturn(PETSC_SUCCESS);
7396   }
7397 
7398   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7399 
7400   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7401 
7402   /* workspaces */
7403   if (numFields) {
7404     for (f = 0; f < numFields; f++) {
7405       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7406       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7407     }
7408   } else {
7409     PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7410     PetscCall(DMGetWorkArray(dm, numPoints, MPIU_INT, &newPointOffsets[0]));
7411   }
7412 
7413   /* get workspaces for the point-to-point matrices */
7414   if (numFields) {
7415     PetscInt totalOffset, totalMatOffset;
7416 
7417     for (p = 0; p < numPoints; p++) {
7418       PetscInt b    = points[2 * p];
7419       PetscInt bDof = 0, bSecDof;
7420 
7421       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7422       if (!bSecDof) {
7423         for (f = 0; f < numFields; f++) {
7424           newPointOffsets[f][p + 1] = 0;
7425           pointMatOffsets[f][p + 1] = 0;
7426         }
7427         continue;
7428       }
7429       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7430       if (bDof) {
7431         for (f = 0; f < numFields; f++) {
7432           PetscInt fDof, q, bOff, allFDof = 0;
7433 
7434           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7435           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7436           for (q = 0; q < bDof; q++) {
7437             PetscInt a = anchors[bOff + q];
7438             PetscInt aFDof;
7439 
7440             PetscCall(PetscSectionGetFieldDof(section, a, f, &aFDof));
7441             allFDof += aFDof;
7442           }
7443           newPointOffsets[f][p + 1] = allFDof;
7444           pointMatOffsets[f][p + 1] = fDof * allFDof;
7445         }
7446       } else {
7447         for (f = 0; f < numFields; f++) {
7448           PetscInt fDof;
7449 
7450           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7451           newPointOffsets[f][p + 1] = fDof;
7452           pointMatOffsets[f][p + 1] = 0;
7453         }
7454       }
7455     }
7456     for (f = 0, totalOffset = 0, totalMatOffset = 0; f < numFields; f++) {
7457       newPointOffsets[f][0] = totalOffset;
7458       pointMatOffsets[f][0] = totalMatOffset;
7459       for (p = 0; p < numPoints; p++) {
7460         newPointOffsets[f][p + 1] += newPointOffsets[f][p];
7461         pointMatOffsets[f][p + 1] += pointMatOffsets[f][p];
7462       }
7463       totalOffset    = newPointOffsets[f][numPoints];
7464       totalMatOffset = pointMatOffsets[f][numPoints];
7465       PetscCall(DMGetWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7466     }
7467   } else {
7468     for (p = 0; p < numPoints; p++) {
7469       PetscInt b    = points[2 * p];
7470       PetscInt bDof = 0, bSecDof;
7471 
7472       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7473       if (!bSecDof) {
7474         newPointOffsets[0][p + 1] = 0;
7475         pointMatOffsets[0][p + 1] = 0;
7476         continue;
7477       }
7478       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7479       if (bDof) {
7480         PetscInt bOff, q, allDof = 0;
7481 
7482         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7483         for (q = 0; q < bDof; q++) {
7484           PetscInt a = anchors[bOff + q], aDof;
7485 
7486           PetscCall(PetscSectionGetDof(section, a, &aDof));
7487           allDof += aDof;
7488         }
7489         newPointOffsets[0][p + 1] = allDof;
7490         pointMatOffsets[0][p + 1] = bSecDof * allDof;
7491       } else {
7492         newPointOffsets[0][p + 1] = bSecDof;
7493         pointMatOffsets[0][p + 1] = 0;
7494       }
7495     }
7496     newPointOffsets[0][0] = 0;
7497     pointMatOffsets[0][0] = 0;
7498     for (p = 0; p < numPoints; p++) {
7499       newPointOffsets[0][p + 1] += newPointOffsets[0][p];
7500       pointMatOffsets[0][p + 1] += pointMatOffsets[0][p];
7501     }
7502     PetscCall(DMGetWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7503   }
7504 
7505   /* output arrays */
7506   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7507 
7508   /* get the point-to-point matrices; construct newPoints */
7509   PetscCall(PetscSectionGetMaxDof(aSec, &maxAnchor));
7510   PetscCall(PetscSectionGetMaxDof(section, &maxDof));
7511   PetscCall(DMGetWorkArray(dm, maxDof, MPIU_INT, &indices));
7512   PetscCall(DMGetWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7513   if (numFields) {
7514     for (p = 0, newP = 0; p < numPoints; p++) {
7515       PetscInt b    = points[2 * p];
7516       PetscInt o    = points[2 * p + 1];
7517       PetscInt bDof = 0, bSecDof;
7518 
7519       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7520       if (!bSecDof) continue;
7521       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7522       if (bDof) {
7523         PetscInt fStart[32], fEnd[32], fAnchorStart[32], fAnchorEnd[32], bOff, q;
7524 
7525         fStart[0] = 0;
7526         fEnd[0]   = 0;
7527         for (f = 0; f < numFields; f++) {
7528           PetscInt fDof;
7529 
7530           PetscCall(PetscSectionGetFieldDof(cSec, b, f, &fDof));
7531           fStart[f + 1] = fStart[f] + fDof;
7532           fEnd[f + 1]   = fStart[f + 1];
7533         }
7534         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7535         PetscCall(DMPlexGetIndicesPointFields_Internal(cSec, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, p, NULL, indices));
7536 
7537         fAnchorStart[0] = 0;
7538         fAnchorEnd[0]   = 0;
7539         for (f = 0; f < numFields; f++) {
7540           PetscInt fDof = newPointOffsets[f][p + 1] - newPointOffsets[f][p];
7541 
7542           fAnchorStart[f + 1] = fAnchorStart[f] + fDof;
7543           fAnchorEnd[f + 1]   = fAnchorStart[f + 1];
7544         }
7545         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7546         for (q = 0; q < bDof; q++) {
7547           PetscInt a = anchors[bOff + q], aOff;
7548 
7549           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7550           newPoints[2 * (newP + q)]     = a;
7551           newPoints[2 * (newP + q) + 1] = 0;
7552           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7553           PetscCall(DMPlexGetIndicesPointFields_Internal(section, PETSC_TRUE, a, aOff, fAnchorEnd, PETSC_TRUE, NULL, -1, NULL, newIndices));
7554         }
7555         newP += bDof;
7556 
7557         if (outValues) {
7558           /* get the point-to-point submatrix */
7559           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]));
7560         }
7561       } else {
7562         newPoints[2 * newP]     = b;
7563         newPoints[2 * newP + 1] = o;
7564         newP++;
7565       }
7566     }
7567   } else {
7568     for (p = 0; p < numPoints; p++) {
7569       PetscInt b    = points[2 * p];
7570       PetscInt o    = points[2 * p + 1];
7571       PetscInt bDof = 0, bSecDof;
7572 
7573       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7574       if (!bSecDof) continue;
7575       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7576       if (bDof) {
7577         PetscInt bEnd = 0, bAnchorEnd = 0, bOff;
7578 
7579         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7580         PetscCall(DMPlexGetIndicesPoint_Internal(cSec, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, indices));
7581 
7582         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7583         for (q = 0; q < bDof; q++) {
7584           PetscInt a = anchors[bOff + q], aOff;
7585 
7586           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7587 
7588           newPoints[2 * (newP + q)]     = a;
7589           newPoints[2 * (newP + q) + 1] = 0;
7590           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7591           PetscCall(DMPlexGetIndicesPoint_Internal(section, PETSC_TRUE, a, aOff, &bAnchorEnd, PETSC_TRUE, NULL, NULL, newIndices));
7592         }
7593         newP += bDof;
7594 
7595         /* get the point-to-point submatrix */
7596         if (outValues) PetscCall(MatGetValues(cMat, bEnd, indices, bAnchorEnd, newIndices, pointMat[0] + pointMatOffsets[0][p]));
7597       } else {
7598         newPoints[2 * newP]     = b;
7599         newPoints[2 * newP + 1] = o;
7600         newP++;
7601       }
7602     }
7603   }
7604 
7605   if (outValues) {
7606     PetscCall(DMGetWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7607     PetscCall(PetscArrayzero(tmpValues, newNumIndices * numIndices));
7608     /* multiply constraints on the right */
7609     if (numFields) {
7610       for (f = 0; f < numFields; f++) {
7611         PetscInt oldOff = offsets[f];
7612 
7613         for (p = 0; p < numPoints; p++) {
7614           PetscInt cStart = newPointOffsets[f][p];
7615           PetscInt b      = points[2 * p];
7616           PetscInt c, r, k;
7617           PetscInt dof;
7618 
7619           PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7620           if (!dof) continue;
7621           if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7622             PetscInt           nCols = newPointOffsets[f][p + 1] - cStart;
7623             const PetscScalar *mat   = pointMat[f] + pointMatOffsets[f][p];
7624 
7625             for (r = 0; r < numIndices; r++) {
7626               for (c = 0; c < nCols; c++) {
7627                 for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += values[r * numIndices + oldOff + k] * mat[k * nCols + c];
7628               }
7629             }
7630           } else {
7631             /* copy this column as is */
7632             for (r = 0; r < numIndices; r++) {
7633               for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7634             }
7635           }
7636           oldOff += dof;
7637         }
7638       }
7639     } else {
7640       PetscInt oldOff = 0;
7641       for (p = 0; p < numPoints; p++) {
7642         PetscInt cStart = newPointOffsets[0][p];
7643         PetscInt b      = points[2 * p];
7644         PetscInt c, r, k;
7645         PetscInt dof;
7646 
7647         PetscCall(PetscSectionGetDof(section, b, &dof));
7648         if (!dof) continue;
7649         if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7650           PetscInt           nCols = newPointOffsets[0][p + 1] - cStart;
7651           const PetscScalar *mat   = pointMat[0] + pointMatOffsets[0][p];
7652 
7653           for (r = 0; r < numIndices; r++) {
7654             for (c = 0; c < nCols; c++) {
7655               for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += mat[k * nCols + c] * values[r * numIndices + oldOff + k];
7656             }
7657           }
7658         } else {
7659           /* copy this column as is */
7660           for (r = 0; r < numIndices; r++) {
7661             for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7662           }
7663         }
7664         oldOff += dof;
7665       }
7666     }
7667 
7668     if (multiplyLeft) {
7669       PetscCall(DMGetWorkArray(dm, newNumIndices * newNumIndices, MPIU_SCALAR, &newValues));
7670       PetscCall(PetscArrayzero(newValues, newNumIndices * newNumIndices));
7671       /* multiply constraints transpose on the left */
7672       if (numFields) {
7673         for (f = 0; f < numFields; f++) {
7674           PetscInt oldOff = offsets[f];
7675 
7676           for (p = 0; p < numPoints; p++) {
7677             PetscInt rStart = newPointOffsets[f][p];
7678             PetscInt b      = points[2 * p];
7679             PetscInt c, r, k;
7680             PetscInt dof;
7681 
7682             PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7683             if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7684               PetscInt                          nRows = newPointOffsets[f][p + 1] - rStart;
7685               const PetscScalar *PETSC_RESTRICT mat   = pointMat[f] + pointMatOffsets[f][p];
7686 
7687               for (r = 0; r < nRows; r++) {
7688                 for (c = 0; c < newNumIndices; c++) {
7689                   for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7690                 }
7691               }
7692             } else {
7693               /* copy this row as is */
7694               for (r = 0; r < dof; r++) {
7695                 for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7696               }
7697             }
7698             oldOff += dof;
7699           }
7700         }
7701       } else {
7702         PetscInt oldOff = 0;
7703 
7704         for (p = 0; p < numPoints; p++) {
7705           PetscInt rStart = newPointOffsets[0][p];
7706           PetscInt b      = points[2 * p];
7707           PetscInt c, r, k;
7708           PetscInt dof;
7709 
7710           PetscCall(PetscSectionGetDof(section, b, &dof));
7711           if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7712             PetscInt                          nRows = newPointOffsets[0][p + 1] - rStart;
7713             const PetscScalar *PETSC_RESTRICT mat   = pointMat[0] + pointMatOffsets[0][p];
7714 
7715             for (r = 0; r < nRows; r++) {
7716               for (c = 0; c < newNumIndices; c++) {
7717                 for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7718               }
7719             }
7720           } else {
7721             /* copy this row as is */
7722             for (r = 0; r < dof; r++) {
7723               for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7724             }
7725           }
7726           oldOff += dof;
7727         }
7728       }
7729 
7730       PetscCall(DMRestoreWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7731     } else {
7732       newValues = tmpValues;
7733     }
7734   }
7735 
7736   /* clean up */
7737   PetscCall(DMRestoreWorkArray(dm, maxDof, MPIU_INT, &indices));
7738   PetscCall(DMRestoreWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7739 
7740   if (numFields) {
7741     for (f = 0; f < numFields; f++) {
7742       PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7743       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7744       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7745     }
7746   } else {
7747     PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7748     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7749     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[0]));
7750   }
7751   PetscCall(ISRestoreIndices(aIS, &anchors));
7752 
7753   /* output */
7754   if (outPoints) {
7755     *outPoints = newPoints;
7756   } else {
7757     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7758   }
7759   if (outValues) *outValues = newValues;
7760   for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7761   PetscFunctionReturn(PETSC_SUCCESS);
7762 }
7763 
7764 /*@C
7765   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
7766 
7767   Not collective
7768 
7769   Input Parameters:
7770 + dm         - The `DM`
7771 . section    - The `PetscSection` describing the points (a local section)
7772 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
7773 . point      - The point defining the closure
7774 - useClPerm  - Use the closure point permutation if available
7775 
7776   Output Parameters:
7777 + numIndices - The number of dof indices in the closure of point with the input sections
7778 . indices    - The dof indices
7779 . outOffsets - Array to write the field offsets into, or `NULL`
7780 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
7781 
7782   Level: advanced
7783 
7784   Notes:
7785   Must call `DMPlexRestoreClosureIndices()` to free allocated memory
7786 
7787   If `idxSection` is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
7788   of those indices is not significant.  If `idxSection` is local, the constrained dofs will yield the involution -(idx+1)
7789   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7790   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when `idxSection` == section, otherwise global
7791   indices (with the above semantics) are implied.
7792 
7793 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
7794           `PetscSection`, `DMGetGlobalSection()`
7795 @*/
7796 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7797 {
7798   /* Closure ordering */
7799   PetscSection    clSection;
7800   IS              clPoints;
7801   const PetscInt *clp;
7802   PetscInt       *points;
7803   const PetscInt *clperm = NULL;
7804   /* Dof permutation and sign flips */
7805   const PetscInt    **perms[32] = {NULL};
7806   const PetscScalar **flips[32] = {NULL};
7807   PetscScalar        *valCopy   = NULL;
7808   /* Hanging node constraints */
7809   PetscInt    *pointsC = NULL;
7810   PetscScalar *valuesC = NULL;
7811   PetscInt     NclC, NiC;
7812 
7813   PetscInt *idx;
7814   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7815   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7816 
7817   PetscFunctionBeginHot;
7818   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7819   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7820   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7821   if (numIndices) PetscAssertPointer(numIndices, 6);
7822   if (indices) PetscAssertPointer(indices, 7);
7823   if (outOffsets) PetscAssertPointer(outOffsets, 8);
7824   if (values) PetscAssertPointer(values, 9);
7825   PetscCall(PetscSectionGetNumFields(section, &Nf));
7826   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7827   PetscCall(PetscArrayzero(offsets, 32));
7828   /* 1) Get points in closure */
7829   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7830   if (useClPerm) {
7831     PetscInt depth, clsize;
7832     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7833     for (clsize = 0, p = 0; p < Ncl; p++) {
7834       PetscInt dof;
7835       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7836       clsize += dof;
7837     }
7838     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7839   }
7840   /* 2) Get number of indices on these points and field offsets from section */
7841   for (p = 0; p < Ncl * 2; p += 2) {
7842     PetscInt dof, fdof;
7843 
7844     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7845     for (f = 0; f < Nf; ++f) {
7846       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7847       offsets[f + 1] += fdof;
7848     }
7849     Ni += dof;
7850   }
7851   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
7852   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7853   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7854   for (f = 0; f < PetscMax(1, Nf); ++f) {
7855     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7856     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
7857     /* may need to apply sign changes to the element matrix */
7858     if (values && flips[f]) {
7859       PetscInt foffset = offsets[f];
7860 
7861       for (p = 0; p < Ncl; ++p) {
7862         PetscInt           pnt  = points[2 * p], fdof;
7863         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
7864 
7865         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
7866         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
7867         if (flip) {
7868           PetscInt i, j, k;
7869 
7870           if (!valCopy) {
7871             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7872             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
7873             *values = valCopy;
7874           }
7875           for (i = 0; i < fdof; ++i) {
7876             PetscScalar fval = flip[i];
7877 
7878             for (k = 0; k < Ni; ++k) {
7879               valCopy[Ni * (foffset + i) + k] *= fval;
7880               valCopy[Ni * k + (foffset + i)] *= fval;
7881             }
7882           }
7883         }
7884         foffset += fdof;
7885       }
7886     }
7887   }
7888   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
7889   PetscCall(DMPlexAnchorsModifyMat(dm, section, Ncl, Ni, points, perms, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, PETSC_TRUE));
7890   if (NclC) {
7891     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7892     for (f = 0; f < PetscMax(1, Nf); ++f) {
7893       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7894       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7895     }
7896     for (f = 0; f < PetscMax(1, Nf); ++f) {
7897       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
7898       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
7899     }
7900     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7901     Ncl    = NclC;
7902     Ni     = NiC;
7903     points = pointsC;
7904     if (values) *values = valuesC;
7905   }
7906   /* 5) Calculate indices */
7907   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
7908   if (Nf) {
7909     PetscInt  idxOff;
7910     PetscBool useFieldOffsets;
7911 
7912     if (outOffsets) {
7913       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
7914     }
7915     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
7916     if (useFieldOffsets) {
7917       for (p = 0; p < Ncl; ++p) {
7918         const PetscInt pnt = points[p * 2];
7919 
7920         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
7921       }
7922     } else {
7923       for (p = 0; p < Ncl; ++p) {
7924         const PetscInt pnt = points[p * 2];
7925 
7926         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7927         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7928          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
7929          * global section. */
7930         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
7931       }
7932     }
7933   } else {
7934     PetscInt off = 0, idxOff;
7935 
7936     for (p = 0; p < Ncl; ++p) {
7937       const PetscInt  pnt  = points[p * 2];
7938       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
7939 
7940       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7941       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7942        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
7943       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
7944     }
7945   }
7946   /* 6) Cleanup */
7947   for (f = 0; f < PetscMax(1, Nf); ++f) {
7948     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7949     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7950   }
7951   if (NclC) {
7952     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
7953   } else {
7954     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7955   }
7956 
7957   if (numIndices) *numIndices = Ni;
7958   if (indices) *indices = idx;
7959   PetscFunctionReturn(PETSC_SUCCESS);
7960 }
7961 
7962 /*@C
7963   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
7964 
7965   Not collective
7966 
7967   Input Parameters:
7968 + dm         - The `DM`
7969 . section    - The `PetscSection` describing the points (a local section)
7970 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
7971 . point      - The point defining the closure
7972 - useClPerm  - Use the closure point permutation if available
7973 
7974   Output Parameters:
7975 + numIndices - The number of dof indices in the closure of point with the input sections
7976 . indices    - The dof indices
7977 . outOffsets - Array to write the field offsets into, or `NULL`
7978 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
7979 
7980   Level: advanced
7981 
7982   Notes:
7983   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
7984 
7985   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
7986   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7987   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7988   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7989   indices (with the above semantics) are implied.
7990 
7991 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7992 @*/
7993 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7994 {
7995   PetscFunctionBegin;
7996   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7997   PetscAssertPointer(indices, 7);
7998   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
7999   PetscFunctionReturn(PETSC_SUCCESS);
8000 }
8001 
8002 PetscErrorCode DMPlexMatSetClosure_Internal(DM dm, PetscSection section, PetscSection globalSection, PetscBool useClPerm, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8003 {
8004   DM_Plex           *mesh = (DM_Plex *)dm->data;
8005   PetscInt          *indices;
8006   PetscInt           numIndices;
8007   const PetscScalar *valuesOrig = values;
8008   PetscErrorCode     ierr;
8009 
8010   PetscFunctionBegin;
8011   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8012   if (!section) PetscCall(DMGetLocalSection(dm, &section));
8013   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
8014   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
8015   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
8016   PetscValidHeaderSpecific(A, MAT_CLASSID, 5);
8017 
8018   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, useClPerm, &numIndices, &indices, NULL, (PetscScalar **)&values));
8019 
8020   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
8021   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8022   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
8023   if (ierr) {
8024     PetscMPIInt rank;
8025 
8026     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8027     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8028     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
8029     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8030     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8031     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
8032   }
8033   if (mesh->printFEM > 1) {
8034     PetscInt i;
8035     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
8036     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
8037     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8038   }
8039 
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   PetscFunctionReturn(PETSC_SUCCESS);
8043 }
8044 
8045 /*@C
8046   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
8047 
8048   Not collective
8049 
8050   Input Parameters:
8051 + dm            - The `DM`
8052 . section       - The section describing the layout in `v`, or `NULL` to use the default section
8053 . globalSection - The section describing the layout in `v`, or `NULL` to use the default global section
8054 . A             - The matrix
8055 . point         - The point in the `DM`
8056 . values        - The array of values
8057 - mode          - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8058 
8059   Level: intermediate
8060 
8061 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8062 @*/
8063 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8064 {
8065   PetscFunctionBegin;
8066   PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, PETSC_TRUE, A, point, values, mode));
8067   PetscFunctionReturn(PETSC_SUCCESS);
8068 }
8069 
8070 /*@C
8071   DMPlexMatSetClosureGeneral - Set an array of the values on the closure of 'point' using a different row and column section
8072 
8073   Not collective
8074 
8075   Input Parameters:
8076 + dmRow            - The `DM` for the row fields
8077 . sectionRow       - The section describing the layout, or `NULL` to use the default section in `dmRow`
8078 . useRowPerm       - The flag to use the closure permutation of the `dmRow` if available
8079 . globalSectionRow - The section describing the layout, or `NULL` to use the default global section in `dmRow`
8080 . dmCol            - The `DM` for the column fields
8081 . sectionCol       - The section describing the layout, or `NULL` to use the default section in `dmCol`
8082 . useColPerm       - The flag to use the closure permutation of the `dmCol` if available
8083 . globalSectionCol - The section describing the layout, or `NULL` to use the default global section in `dmCol`
8084 . A                - The matrix
8085 . point            - The point in the `DM`
8086 . values           - The array of values
8087 - mode             - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8088 
8089   Level: intermediate
8090 
8091 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8092 @*/
8093 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)
8094 {
8095   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
8096   PetscInt          *indicesRow, *indicesCol;
8097   PetscInt           numIndicesRow, numIndicesCol;
8098   const PetscScalar *valuesOrig = values;
8099   PetscErrorCode     ierr;
8100 
8101   PetscFunctionBegin;
8102   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
8103   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
8104   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
8105   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
8106   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
8107   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 5);
8108   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
8109   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 6);
8110   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
8111   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 7);
8112   PetscValidHeaderSpecific(A, MAT_CLASSID, 9);
8113 
8114   PetscCall(DMPlexGetClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
8115   PetscCall(DMPlexGetClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
8116 
8117   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
8118   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8119   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values, mode);
8120   if (ierr) {
8121     PetscMPIInt rank;
8122 
8123     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8124     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8125     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
8126     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
8127     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&values));
8128     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
8129   }
8130 
8131   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
8132   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
8133   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
8134   PetscFunctionReturn(PETSC_SUCCESS);
8135 }
8136 
8137 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8138 {
8139   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
8140   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8141   PetscInt       *cpoints = NULL;
8142   PetscInt       *findices, *cindices;
8143   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8144   PetscInt        foffsets[32], coffsets[32];
8145   DMPolytopeType  ct;
8146   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8147   PetscErrorCode  ierr;
8148 
8149   PetscFunctionBegin;
8150   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8151   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8152   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8153   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8154   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8155   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8156   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8157   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8158   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8159   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8160   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
8161   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8162   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8163   PetscCall(PetscArrayzero(foffsets, 32));
8164   PetscCall(PetscArrayzero(coffsets, 32));
8165   /* Column indices */
8166   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8167   maxFPoints = numCPoints;
8168   /* Compress out points not in the section */
8169   /*   TODO: Squeeze out points with 0 dof as well */
8170   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8171   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8172     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8173       cpoints[q * 2]     = cpoints[p];
8174       cpoints[q * 2 + 1] = cpoints[p + 1];
8175       ++q;
8176     }
8177   }
8178   numCPoints = q;
8179   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8180     PetscInt fdof;
8181 
8182     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8183     if (!dof) continue;
8184     for (f = 0; f < numFields; ++f) {
8185       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8186       coffsets[f + 1] += fdof;
8187     }
8188     numCIndices += dof;
8189   }
8190   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8191   /* Row indices */
8192   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8193   {
8194     DMPlexTransform tr;
8195     DMPolytopeType *rct;
8196     PetscInt       *rsize, *rcone, *rornt, Nt;
8197 
8198     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8199     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8200     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8201     numSubcells = rsize[Nt - 1];
8202     PetscCall(DMPlexTransformDestroy(&tr));
8203   }
8204   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8205   for (r = 0, q = 0; r < numSubcells; ++r) {
8206     /* TODO Map from coarse to fine cells */
8207     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8208     /* Compress out points not in the section */
8209     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8210     for (p = 0; p < numFPoints * 2; p += 2) {
8211       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8212         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8213         if (!dof) continue;
8214         for (s = 0; s < q; ++s)
8215           if (fpoints[p] == ftotpoints[s * 2]) break;
8216         if (s < q) continue;
8217         ftotpoints[q * 2]     = fpoints[p];
8218         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8219         ++q;
8220       }
8221     }
8222     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8223   }
8224   numFPoints = q;
8225   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8226     PetscInt fdof;
8227 
8228     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8229     if (!dof) continue;
8230     for (f = 0; f < numFields; ++f) {
8231       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8232       foffsets[f + 1] += fdof;
8233     }
8234     numFIndices += dof;
8235   }
8236   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8237 
8238   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8239   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8240   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8241   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8242   if (numFields) {
8243     const PetscInt **permsF[32] = {NULL};
8244     const PetscInt **permsC[32] = {NULL};
8245 
8246     for (f = 0; f < numFields; f++) {
8247       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8248       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8249     }
8250     for (p = 0; p < numFPoints; p++) {
8251       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8252       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8253     }
8254     for (p = 0; p < numCPoints; p++) {
8255       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8256       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8257     }
8258     for (f = 0; f < numFields; f++) {
8259       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8260       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8261     }
8262   } else {
8263     const PetscInt **permsF = NULL;
8264     const PetscInt **permsC = NULL;
8265 
8266     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8267     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8268     for (p = 0, off = 0; p < numFPoints; p++) {
8269       const PetscInt *perm = permsF ? permsF[p] : NULL;
8270 
8271       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8272       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8273     }
8274     for (p = 0, off = 0; p < numCPoints; p++) {
8275       const PetscInt *perm = permsC ? permsC[p] : NULL;
8276 
8277       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8278       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8279     }
8280     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8281     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8282   }
8283   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8284   /* TODO: flips */
8285   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8286   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8287   if (ierr) {
8288     PetscMPIInt rank;
8289 
8290     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8291     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8292     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8293     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8294     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8295   }
8296   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8297   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8298   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8299   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8300   PetscFunctionReturn(PETSC_SUCCESS);
8301 }
8302 
8303 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8304 {
8305   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8306   PetscInt       *cpoints = NULL;
8307   PetscInt        foffsets[32], coffsets[32];
8308   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8309   DMPolytopeType  ct;
8310   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8311 
8312   PetscFunctionBegin;
8313   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8314   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8315   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8316   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8317   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8318   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8319   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8320   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8321   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8322   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8323   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8324   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8325   PetscCall(PetscArrayzero(foffsets, 32));
8326   PetscCall(PetscArrayzero(coffsets, 32));
8327   /* Column indices */
8328   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8329   maxFPoints = numCPoints;
8330   /* Compress out points not in the section */
8331   /*   TODO: Squeeze out points with 0 dof as well */
8332   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8333   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8334     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8335       cpoints[q * 2]     = cpoints[p];
8336       cpoints[q * 2 + 1] = cpoints[p + 1];
8337       ++q;
8338     }
8339   }
8340   numCPoints = q;
8341   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8342     PetscInt fdof;
8343 
8344     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8345     if (!dof) continue;
8346     for (f = 0; f < numFields; ++f) {
8347       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8348       coffsets[f + 1] += fdof;
8349     }
8350     numCIndices += dof;
8351   }
8352   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8353   /* Row indices */
8354   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8355   {
8356     DMPlexTransform tr;
8357     DMPolytopeType *rct;
8358     PetscInt       *rsize, *rcone, *rornt, Nt;
8359 
8360     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8361     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8362     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8363     numSubcells = rsize[Nt - 1];
8364     PetscCall(DMPlexTransformDestroy(&tr));
8365   }
8366   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8367   for (r = 0, q = 0; r < numSubcells; ++r) {
8368     /* TODO Map from coarse to fine cells */
8369     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8370     /* Compress out points not in the section */
8371     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8372     for (p = 0; p < numFPoints * 2; p += 2) {
8373       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8374         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8375         if (!dof) continue;
8376         for (s = 0; s < q; ++s)
8377           if (fpoints[p] == ftotpoints[s * 2]) break;
8378         if (s < q) continue;
8379         ftotpoints[q * 2]     = fpoints[p];
8380         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8381         ++q;
8382       }
8383     }
8384     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8385   }
8386   numFPoints = q;
8387   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8388     PetscInt fdof;
8389 
8390     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8391     if (!dof) continue;
8392     for (f = 0; f < numFields; ++f) {
8393       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8394       foffsets[f + 1] += fdof;
8395     }
8396     numFIndices += dof;
8397   }
8398   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8399 
8400   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8401   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8402   if (numFields) {
8403     const PetscInt **permsF[32] = {NULL};
8404     const PetscInt **permsC[32] = {NULL};
8405 
8406     for (f = 0; f < numFields; f++) {
8407       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8408       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8409     }
8410     for (p = 0; p < numFPoints; p++) {
8411       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8412       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8413     }
8414     for (p = 0; p < numCPoints; p++) {
8415       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8416       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8417     }
8418     for (f = 0; f < numFields; f++) {
8419       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8420       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8421     }
8422   } else {
8423     const PetscInt **permsF = NULL;
8424     const PetscInt **permsC = NULL;
8425 
8426     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8427     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8428     for (p = 0, off = 0; p < numFPoints; p++) {
8429       const PetscInt *perm = permsF ? permsF[p] : NULL;
8430 
8431       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8432       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8433     }
8434     for (p = 0, off = 0; p < numCPoints; p++) {
8435       const PetscInt *perm = permsC ? permsC[p] : NULL;
8436 
8437       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8438       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8439     }
8440     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8441     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8442   }
8443   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8444   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8445   PetscFunctionReturn(PETSC_SUCCESS);
8446 }
8447 
8448 /*@C
8449   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8450 
8451   Input Parameter:
8452 . dm - The `DMPLEX` object
8453 
8454   Output Parameter:
8455 . cellHeight - The height of a cell
8456 
8457   Level: developer
8458 
8459 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetVTKCellHeight()`
8460 @*/
8461 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8462 {
8463   DM_Plex *mesh = (DM_Plex *)dm->data;
8464 
8465   PetscFunctionBegin;
8466   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8467   PetscAssertPointer(cellHeight, 2);
8468   *cellHeight = mesh->vtkCellHeight;
8469   PetscFunctionReturn(PETSC_SUCCESS);
8470 }
8471 
8472 /*@C
8473   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8474 
8475   Input Parameters:
8476 + dm         - The `DMPLEX` object
8477 - cellHeight - The height of a cell
8478 
8479   Level: developer
8480 
8481 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8482 @*/
8483 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8484 {
8485   DM_Plex *mesh = (DM_Plex *)dm->data;
8486 
8487   PetscFunctionBegin;
8488   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8489   mesh->vtkCellHeight = cellHeight;
8490   PetscFunctionReturn(PETSC_SUCCESS);
8491 }
8492 
8493 /*@
8494   DMPlexGetCellTypeStratum - Get the range of cells of a given celltype
8495 
8496   Input Parameters:
8497 + dm - The `DMPLEX` object
8498 - ct - The `DMPolytopeType` of the cell
8499 
8500   Output Parameters:
8501 + start - The first cell of this type, or `NULL`
8502 - end   - The upper bound on this celltype, or `NULL`
8503 
8504   Level: advanced
8505 
8506 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8507 @*/
8508 PetscErrorCode DMPlexGetCellTypeStratum(DM dm, DMPolytopeType ct, PetscInt *start, PetscInt *end)
8509 {
8510   DM_Plex *mesh = (DM_Plex *)dm->data;
8511   DMLabel  label;
8512   PetscInt pStart, pEnd;
8513 
8514   PetscFunctionBegin;
8515   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8516   if (start) {
8517     PetscAssertPointer(start, 3);
8518     *start = 0;
8519   }
8520   if (end) {
8521     PetscAssertPointer(end, 4);
8522     *end = 0;
8523   }
8524   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8525   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
8526   if (mesh->tr) {
8527     PetscCall(DMPlexTransformGetCellTypeStratum(mesh->tr, ct, start, end));
8528   } else {
8529     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
8530     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named celltype was found");
8531     PetscCall(DMLabelGetStratumBounds(label, ct, start, end));
8532   }
8533   PetscFunctionReturn(PETSC_SUCCESS);
8534 }
8535 
8536 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8537 {
8538   PetscSection section, globalSection;
8539   PetscInt    *numbers, p;
8540 
8541   PetscFunctionBegin;
8542   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8543   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8544   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8545   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8546   PetscCall(PetscSectionSetUp(section));
8547   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_FALSE, &globalSection));
8548   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8549   for (p = pStart; p < pEnd; ++p) {
8550     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8551     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8552     else numbers[p - pStart] += shift;
8553   }
8554   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8555   if (globalSize) {
8556     PetscLayout layout;
8557     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8558     PetscCall(PetscLayoutGetSize(layout, globalSize));
8559     PetscCall(PetscLayoutDestroy(&layout));
8560   }
8561   PetscCall(PetscSectionDestroy(&section));
8562   PetscCall(PetscSectionDestroy(&globalSection));
8563   PetscFunctionReturn(PETSC_SUCCESS);
8564 }
8565 
8566 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
8567 {
8568   PetscInt cellHeight, cStart, cEnd;
8569 
8570   PetscFunctionBegin;
8571   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8572   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8573   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8574   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8575   PetscFunctionReturn(PETSC_SUCCESS);
8576 }
8577 
8578 /*@
8579   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8580 
8581   Input Parameter:
8582 . dm - The `DMPLEX` object
8583 
8584   Output Parameter:
8585 . globalCellNumbers - Global cell numbers for all cells on this process
8586 
8587   Level: developer
8588 
8589 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVertexNumbering()`
8590 @*/
8591 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8592 {
8593   DM_Plex *mesh = (DM_Plex *)dm->data;
8594 
8595   PetscFunctionBegin;
8596   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8597   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8598   *globalCellNumbers = mesh->globalCellNumbers;
8599   PetscFunctionReturn(PETSC_SUCCESS);
8600 }
8601 
8602 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8603 {
8604   PetscInt vStart, vEnd;
8605 
8606   PetscFunctionBegin;
8607   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8608   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8609   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8610   PetscFunctionReturn(PETSC_SUCCESS);
8611 }
8612 
8613 /*@
8614   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8615 
8616   Input Parameter:
8617 . dm - The `DMPLEX` object
8618 
8619   Output Parameter:
8620 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8621 
8622   Level: developer
8623 
8624 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8625 @*/
8626 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8627 {
8628   DM_Plex *mesh = (DM_Plex *)dm->data;
8629 
8630   PetscFunctionBegin;
8631   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8632   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8633   *globalVertexNumbers = mesh->globalVertexNumbers;
8634   PetscFunctionReturn(PETSC_SUCCESS);
8635 }
8636 
8637 /*@
8638   DMPlexCreatePointNumbering - Create a global numbering for all points.
8639 
8640   Collective
8641 
8642   Input Parameter:
8643 . dm - The `DMPLEX` object
8644 
8645   Output Parameter:
8646 . globalPointNumbers - Global numbers for all points on this process
8647 
8648   Level: developer
8649 
8650   Notes:
8651   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8652   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8653   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8654   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8655 
8656   The partitioned mesh is
8657   ```
8658   (2)--0--(3)--1--(4)    (1)--0--(2)
8659   ```
8660   and its global numbering is
8661   ```
8662   (3)--0--(4)--1--(5)--2--(6)
8663   ```
8664   Then the global numbering is provided as
8665   ```
8666   [0] Number of indices in set 5
8667   [0] 0 0
8668   [0] 1 1
8669   [0] 2 3
8670   [0] 3 4
8671   [0] 4 -6
8672   [1] Number of indices in set 3
8673   [1] 0 2
8674   [1] 1 5
8675   [1] 2 6
8676   ```
8677 
8678 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8679 @*/
8680 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8681 {
8682   IS        nums[4];
8683   PetscInt  depths[4], gdepths[4], starts[4];
8684   PetscInt  depth, d, shift = 0;
8685   PetscBool empty = PETSC_FALSE;
8686 
8687   PetscFunctionBegin;
8688   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8689   PetscCall(DMPlexGetDepth(dm, &depth));
8690   // For unstratified meshes use dim instead of depth
8691   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8692   // If any stratum is empty, we must mark all empty
8693   for (d = 0; d <= depth; ++d) {
8694     PetscInt end;
8695 
8696     depths[d] = depth - d;
8697     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8698     if (!(starts[d] - end)) empty = PETSC_TRUE;
8699   }
8700   if (empty)
8701     for (d = 0; d <= depth; ++d) {
8702       depths[d] = -1;
8703       starts[d] = -1;
8704     }
8705   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8706   PetscCall(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8707   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]);
8708   // Note here that 'shift' is collective, so that the numbering is stratified by depth
8709   for (d = 0; d <= depth; ++d) {
8710     PetscInt pStart, pEnd, gsize;
8711 
8712     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8713     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8714     shift += gsize;
8715   }
8716   PetscCall(ISConcatenate(PETSC_COMM_SELF, depth + 1, nums, globalPointNumbers));
8717   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8718   PetscFunctionReturn(PETSC_SUCCESS);
8719 }
8720 
8721 /*@
8722   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8723 
8724   Input Parameter:
8725 . dm - The `DMPLEX` object
8726 
8727   Output Parameter:
8728 . ranks - The rank field
8729 
8730   Options Database Key:
8731 . -dm_partition_view - Adds the rank field into the `DM` output from `-dm_view` using the same viewer
8732 
8733   Level: intermediate
8734 
8735 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8736 @*/
8737 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8738 {
8739   DM             rdm;
8740   PetscFE        fe;
8741   PetscScalar   *r;
8742   PetscMPIInt    rank;
8743   DMPolytopeType ct;
8744   PetscInt       dim, cStart, cEnd, c;
8745   PetscBool      simplex;
8746 
8747   PetscFunctionBeginUser;
8748   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8749   PetscAssertPointer(ranks, 2);
8750   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
8751   PetscCall(DMClone(dm, &rdm));
8752   PetscCall(DMGetDimension(rdm, &dim));
8753   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8754   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8755   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
8756   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8757   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
8758   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8759   PetscCall(PetscFEDestroy(&fe));
8760   PetscCall(DMCreateDS(rdm));
8761   PetscCall(DMCreateGlobalVector(rdm, ranks));
8762   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
8763   PetscCall(VecGetArray(*ranks, &r));
8764   for (c = cStart; c < cEnd; ++c) {
8765     PetscScalar *lr;
8766 
8767     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8768     if (lr) *lr = rank;
8769   }
8770   PetscCall(VecRestoreArray(*ranks, &r));
8771   PetscCall(DMDestroy(&rdm));
8772   PetscFunctionReturn(PETSC_SUCCESS);
8773 }
8774 
8775 /*@
8776   DMPlexCreateLabelField - Create a cell field whose value is the label value for that cell
8777 
8778   Input Parameters:
8779 + dm    - The `DMPLEX`
8780 - label - The `DMLabel`
8781 
8782   Output Parameter:
8783 . val - The label value field
8784 
8785   Options Database Key:
8786 . -dm_label_view - Adds the label value field into the `DM` output from `-dm_view` using the same viewer
8787 
8788   Level: intermediate
8789 
8790 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8791 @*/
8792 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
8793 {
8794   DM           rdm;
8795   PetscFE      fe;
8796   PetscScalar *v;
8797   PetscInt     dim, cStart, cEnd, c;
8798 
8799   PetscFunctionBeginUser;
8800   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8801   PetscAssertPointer(label, 2);
8802   PetscAssertPointer(val, 3);
8803   PetscCall(DMClone(dm, &rdm));
8804   PetscCall(DMGetDimension(rdm, &dim));
8805   PetscCall(PetscFECreateDefault(PetscObjectComm((PetscObject)rdm), dim, 1, PETSC_TRUE, "PETSc___label_value_", -1, &fe));
8806   PetscCall(PetscObjectSetName((PetscObject)fe, "label_value"));
8807   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8808   PetscCall(PetscFEDestroy(&fe));
8809   PetscCall(DMCreateDS(rdm));
8810   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8811   PetscCall(DMCreateGlobalVector(rdm, val));
8812   PetscCall(PetscObjectSetName((PetscObject)*val, "label_value"));
8813   PetscCall(VecGetArray(*val, &v));
8814   for (c = cStart; c < cEnd; ++c) {
8815     PetscScalar *lv;
8816     PetscInt     cval;
8817 
8818     PetscCall(DMPlexPointGlobalRef(rdm, c, v, &lv));
8819     PetscCall(DMLabelGetValue(label, c, &cval));
8820     *lv = cval;
8821   }
8822   PetscCall(VecRestoreArray(*val, &v));
8823   PetscCall(DMDestroy(&rdm));
8824   PetscFunctionReturn(PETSC_SUCCESS);
8825 }
8826 
8827 /*@
8828   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
8829 
8830   Input Parameter:
8831 . dm - The `DMPLEX` object
8832 
8833   Level: developer
8834 
8835   Notes:
8836   This is a useful diagnostic when creating meshes programmatically.
8837 
8838   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8839 
8840 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8841 @*/
8842 PetscErrorCode DMPlexCheckSymmetry(DM dm)
8843 {
8844   PetscSection    coneSection, supportSection;
8845   const PetscInt *cone, *support;
8846   PetscInt        coneSize, c, supportSize, s;
8847   PetscInt        pStart, pEnd, p, pp, csize, ssize;
8848   PetscBool       storagecheck = PETSC_TRUE;
8849 
8850   PetscFunctionBegin;
8851   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8852   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
8853   PetscCall(DMPlexGetConeSection(dm, &coneSection));
8854   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
8855   /* Check that point p is found in the support of its cone points, and vice versa */
8856   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8857   for (p = pStart; p < pEnd; ++p) {
8858     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
8859     PetscCall(DMPlexGetCone(dm, p, &cone));
8860     for (c = 0; c < coneSize; ++c) {
8861       PetscBool dup = PETSC_FALSE;
8862       PetscInt  d;
8863       for (d = c - 1; d >= 0; --d) {
8864         if (cone[c] == cone[d]) {
8865           dup = PETSC_TRUE;
8866           break;
8867         }
8868       }
8869       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
8870       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
8871       for (s = 0; s < supportSize; ++s) {
8872         if (support[s] == p) break;
8873       }
8874       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
8875         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
8876         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
8877         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8878         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
8879         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
8880         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8881         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]);
8882         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
8883       }
8884     }
8885     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
8886     if (p != pp) {
8887       storagecheck = PETSC_FALSE;
8888       continue;
8889     }
8890     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
8891     PetscCall(DMPlexGetSupport(dm, p, &support));
8892     for (s = 0; s < supportSize; ++s) {
8893       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
8894       PetscCall(DMPlexGetCone(dm, support[s], &cone));
8895       for (c = 0; c < coneSize; ++c) {
8896         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
8897         if (cone[c] != pp) {
8898           c = 0;
8899           break;
8900         }
8901         if (cone[c] == p) break;
8902       }
8903       if (c >= coneSize) {
8904         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
8905         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
8906         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8907         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
8908         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
8909         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8910         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
8911       }
8912     }
8913   }
8914   if (storagecheck) {
8915     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
8916     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
8917     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
8918   }
8919   PetscFunctionReturn(PETSC_SUCCESS);
8920 }
8921 
8922 /*
8923   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.
8924 */
8925 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
8926 {
8927   DMPolytopeType  cct;
8928   PetscInt        ptpoints[4];
8929   const PetscInt *cone, *ccone, *ptcone;
8930   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
8931 
8932   PetscFunctionBegin;
8933   *unsplit = 0;
8934   switch (ct) {
8935   case DM_POLYTOPE_POINT_PRISM_TENSOR:
8936     ptpoints[npt++] = c;
8937     break;
8938   case DM_POLYTOPE_SEG_PRISM_TENSOR:
8939     PetscCall(DMPlexGetCone(dm, c, &cone));
8940     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8941     for (cp = 0; cp < coneSize; ++cp) {
8942       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
8943       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
8944     }
8945     break;
8946   case DM_POLYTOPE_TRI_PRISM_TENSOR:
8947   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8948     PetscCall(DMPlexGetCone(dm, c, &cone));
8949     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8950     for (cp = 0; cp < coneSize; ++cp) {
8951       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
8952       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
8953       for (ccp = 0; ccp < cconeSize; ++ccp) {
8954         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
8955         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
8956           PetscInt p;
8957           for (p = 0; p < npt; ++p)
8958             if (ptpoints[p] == ccone[ccp]) break;
8959           if (p == npt) ptpoints[npt++] = ccone[ccp];
8960         }
8961       }
8962     }
8963     break;
8964   default:
8965     break;
8966   }
8967   for (pt = 0; pt < npt; ++pt) {
8968     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
8969     if (ptcone[0] == ptcone[1]) ++(*unsplit);
8970   }
8971   PetscFunctionReturn(PETSC_SUCCESS);
8972 }
8973 
8974 /*@
8975   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
8976 
8977   Input Parameters:
8978 + dm         - The `DMPLEX` object
8979 - cellHeight - Normally 0
8980 
8981   Level: developer
8982 
8983   Notes:
8984   This is a useful diagnostic when creating meshes programmatically.
8985   Currently applicable only to homogeneous simplex or tensor meshes.
8986 
8987   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8988 
8989 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8990 @*/
8991 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
8992 {
8993   DMPlexInterpolatedFlag interp;
8994   DMPolytopeType         ct;
8995   PetscInt               vStart, vEnd, cStart, cEnd, c;
8996 
8997   PetscFunctionBegin;
8998   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8999   PetscCall(DMPlexIsInterpolated(dm, &interp));
9000   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9001   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9002   for (c = cStart; c < cEnd; ++c) {
9003     PetscInt *closure = NULL;
9004     PetscInt  coneSize, closureSize, cl, Nv = 0;
9005 
9006     PetscCall(DMPlexGetCellType(dm, c, &ct));
9007     PetscCheck((PetscInt)ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
9008     if (ct == DM_POLYTOPE_UNKNOWN) continue;
9009     if (interp == DMPLEX_INTERPOLATED_FULL) {
9010       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9011       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));
9012     }
9013     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9014     for (cl = 0; cl < closureSize * 2; cl += 2) {
9015       const PetscInt p = closure[cl];
9016       if ((p >= vStart) && (p < vEnd)) ++Nv;
9017     }
9018     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9019     /* Special Case: Tensor faces with identified vertices */
9020     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
9021       PetscInt unsplit;
9022 
9023       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9024       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
9025     }
9026     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));
9027   }
9028   PetscFunctionReturn(PETSC_SUCCESS);
9029 }
9030 
9031 /*@
9032   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
9033 
9034   Collective
9035 
9036   Input Parameters:
9037 + dm         - The `DMPLEX` object
9038 - cellHeight - Normally 0
9039 
9040   Level: developer
9041 
9042   Notes:
9043   This is a useful diagnostic when creating meshes programmatically.
9044   This routine is only relevant for meshes that are fully interpolated across all ranks.
9045   It will error out if a partially interpolated mesh is given on some rank.
9046   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
9047 
9048   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9049 
9050 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
9051 @*/
9052 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
9053 {
9054   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
9055   DMPlexInterpolatedFlag interpEnum;
9056 
9057   PetscFunctionBegin;
9058   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9059   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
9060   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(PETSC_SUCCESS);
9061   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
9062     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported"));
9063     PetscFunctionReturn(PETSC_SUCCESS);
9064   }
9065 
9066   PetscCall(DMGetDimension(dm, &dim));
9067   PetscCall(DMPlexGetDepth(dm, &depth));
9068   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9069   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
9070     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
9071     for (c = cStart; c < cEnd; ++c) {
9072       const PetscInt       *cone, *ornt, *faceSizes, *faces;
9073       const DMPolytopeType *faceTypes;
9074       DMPolytopeType        ct;
9075       PetscInt              numFaces, coneSize, f;
9076       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
9077 
9078       PetscCall(DMPlexGetCellType(dm, c, &ct));
9079       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9080       if (unsplit) continue;
9081       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9082       PetscCall(DMPlexGetCone(dm, c, &cone));
9083       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
9084       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9085       for (cl = 0; cl < closureSize * 2; cl += 2) {
9086         const PetscInt p = closure[cl];
9087         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
9088       }
9089       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9090       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);
9091       for (f = 0; f < numFaces; ++f) {
9092         DMPolytopeType fct;
9093         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
9094 
9095         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
9096         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
9097         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
9098           const PetscInt p = fclosure[cl];
9099           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
9100         }
9101         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]);
9102         for (v = 0; v < fnumCorners; ++v) {
9103           if (fclosure[v] != faces[fOff + v]) {
9104             PetscInt v1;
9105 
9106             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
9107             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
9108             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
9109             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
9110             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9111             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]);
9112           }
9113         }
9114         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
9115         fOff += faceSizes[f];
9116       }
9117       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9118       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9119     }
9120   }
9121   PetscFunctionReturn(PETSC_SUCCESS);
9122 }
9123 
9124 /*@
9125   DMPlexCheckGeometry - Check the geometry of mesh cells
9126 
9127   Input Parameter:
9128 . dm - The `DMPLEX` object
9129 
9130   Level: developer
9131 
9132   Notes:
9133   This is a useful diagnostic when creating meshes programmatically.
9134 
9135   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9136 
9137 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9138 @*/
9139 PetscErrorCode DMPlexCheckGeometry(DM dm)
9140 {
9141   Vec       coordinates;
9142   PetscReal detJ, J[9], refVol = 1.0;
9143   PetscReal vol;
9144   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
9145 
9146   PetscFunctionBegin;
9147   PetscCall(DMGetDimension(dm, &dim));
9148   PetscCall(DMGetCoordinateDim(dm, &dE));
9149   if (dim != dE) PetscFunctionReturn(PETSC_SUCCESS);
9150   PetscCall(DMPlexGetDepth(dm, &depth));
9151   for (d = 0; d < dim; ++d) refVol *= 2.0;
9152   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9153   /* Make sure local coordinates are created, because that step is collective */
9154   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
9155   if (!coordinates) PetscFunctionReturn(PETSC_SUCCESS);
9156   for (c = cStart; c < cEnd; ++c) {
9157     DMPolytopeType ct;
9158     PetscInt       unsplit;
9159     PetscBool      ignoreZeroVol = PETSC_FALSE;
9160 
9161     PetscCall(DMPlexGetCellType(dm, c, &ct));
9162     switch (ct) {
9163     case DM_POLYTOPE_SEG_PRISM_TENSOR:
9164     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9165     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9166       ignoreZeroVol = PETSC_TRUE;
9167       break;
9168     default:
9169       break;
9170     }
9171     switch (ct) {
9172     case DM_POLYTOPE_TRI_PRISM:
9173     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9174     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9175     case DM_POLYTOPE_PYRAMID:
9176       continue;
9177     default:
9178       break;
9179     }
9180     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9181     if (unsplit) continue;
9182     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
9183     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);
9184     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
9185     /* This should work with periodicity since DG coordinates should be used */
9186     if (depth > 1) {
9187       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
9188       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);
9189       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
9190     }
9191   }
9192   PetscFunctionReturn(PETSC_SUCCESS);
9193 }
9194 
9195 /*@
9196   DMPlexCheckPointSF - Check that several necessary conditions are met for the point `PetscSF` of this plex.
9197 
9198   Collective
9199 
9200   Input Parameters:
9201 + dm              - The `DMPLEX` object
9202 . pointSF         - The `PetscSF`, or `NULL` for `PointSF` attached to `DM`
9203 - allowExtraRoots - Flag to allow extra points not present in the `DM`
9204 
9205   Level: developer
9206 
9207   Notes:
9208   This is mainly intended for debugging/testing purposes.
9209 
9210   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9211 
9212   Extra roots can come from periodic cuts, where additional points appear on the boundary
9213 
9214 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
9215 @*/
9216 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
9217 {
9218   PetscInt           l, nleaves, nroots, overlap;
9219   const PetscInt    *locals;
9220   const PetscSFNode *remotes;
9221   PetscBool          distributed;
9222   MPI_Comm           comm;
9223   PetscMPIInt        rank;
9224 
9225   PetscFunctionBegin;
9226   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9227   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
9228   else pointSF = dm->sf;
9229   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9230   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
9231   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9232   {
9233     PetscMPIInt mpiFlag;
9234 
9235     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
9236     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
9237   }
9238   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
9239   PetscCall(DMPlexIsDistributed(dm, &distributed));
9240   if (!distributed) {
9241     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);
9242     PetscFunctionReturn(PETSC_SUCCESS);
9243   }
9244   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);
9245   PetscCall(DMPlexGetOverlap(dm, &overlap));
9246 
9247   /* Check SF graph is compatible with DMPlex chart */
9248   {
9249     PetscInt pStart, pEnd, maxLeaf;
9250 
9251     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9252     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9253     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9254     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9255   }
9256 
9257   /* Check Point SF has no local points referenced */
9258   for (l = 0; l < nleaves; l++) {
9259     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);
9260   }
9261 
9262   /* Check there are no cells in interface */
9263   if (!overlap) {
9264     PetscInt cellHeight, cStart, cEnd;
9265 
9266     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9267     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9268     for (l = 0; l < nleaves; ++l) {
9269       const PetscInt point = locals ? locals[l] : l;
9270 
9271       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9272     }
9273   }
9274 
9275   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9276   {
9277     const PetscInt *rootdegree;
9278 
9279     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9280     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9281     for (l = 0; l < nleaves; ++l) {
9282       const PetscInt  point = locals ? locals[l] : l;
9283       const PetscInt *cone;
9284       PetscInt        coneSize, c, idx;
9285 
9286       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9287       PetscCall(DMPlexGetCone(dm, point, &cone));
9288       for (c = 0; c < coneSize; ++c) {
9289         if (!rootdegree[cone[c]]) {
9290           if (locals) {
9291             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9292           } else {
9293             idx = (cone[c] < nleaves) ? cone[c] : -1;
9294           }
9295           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9296         }
9297       }
9298     }
9299   }
9300   PetscFunctionReturn(PETSC_SUCCESS);
9301 }
9302 
9303 /*@
9304   DMPlexCheck - Perform various checks of `DMPLEX` sanity
9305 
9306   Input Parameter:
9307 . dm - The `DMPLEX` object
9308 
9309   Level: developer
9310 
9311   Notes:
9312   This is a useful diagnostic when creating meshes programmatically.
9313 
9314   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9315 
9316   Currently does not include `DMPlexCheckCellShape()`.
9317 
9318 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9319 @*/
9320 PetscErrorCode DMPlexCheck(DM dm)
9321 {
9322   PetscInt cellHeight;
9323 
9324   PetscFunctionBegin;
9325   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9326   PetscCall(DMPlexCheckSymmetry(dm));
9327   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9328   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9329   PetscCall(DMPlexCheckGeometry(dm));
9330   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9331   PetscCall(DMPlexCheckInterfaceCones(dm));
9332   PetscFunctionReturn(PETSC_SUCCESS);
9333 }
9334 
9335 typedef struct cell_stats {
9336   PetscReal min, max, sum, squaresum;
9337   PetscInt  count;
9338 } cell_stats_t;
9339 
9340 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9341 {
9342   PetscInt i, N = *len;
9343 
9344   for (i = 0; i < N; i++) {
9345     cell_stats_t *A = (cell_stats_t *)a;
9346     cell_stats_t *B = (cell_stats_t *)b;
9347 
9348     B->min = PetscMin(A->min, B->min);
9349     B->max = PetscMax(A->max, B->max);
9350     B->sum += A->sum;
9351     B->squaresum += A->squaresum;
9352     B->count += A->count;
9353   }
9354 }
9355 
9356 /*@
9357   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9358 
9359   Collective
9360 
9361   Input Parameters:
9362 + dm        - The `DMPLEX` object
9363 . output    - If true, statistics will be displayed on `stdout`
9364 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9365 
9366   Level: developer
9367 
9368   Notes:
9369   This is mainly intended for debugging/testing purposes.
9370 
9371   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9372 
9373 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9374 @*/
9375 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9376 {
9377   DM           dmCoarse;
9378   cell_stats_t stats, globalStats;
9379   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9380   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9381   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9382   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9383   PetscMPIInt  rank, size;
9384 
9385   PetscFunctionBegin;
9386   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9387   stats.min = PETSC_MAX_REAL;
9388   stats.max = PETSC_MIN_REAL;
9389   stats.sum = stats.squaresum = 0.;
9390   stats.count                 = 0;
9391 
9392   PetscCallMPI(MPI_Comm_size(comm, &size));
9393   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9394   PetscCall(DMGetCoordinateDim(dm, &cdim));
9395   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9396   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9397   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9398   for (c = cStart; c < cEnd; c++) {
9399     PetscInt  i;
9400     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9401 
9402     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9403     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9404     for (i = 0; i < PetscSqr(cdim); ++i) {
9405       frobJ += J[i] * J[i];
9406       frobInvJ += invJ[i] * invJ[i];
9407     }
9408     cond2 = frobJ * frobInvJ;
9409     cond  = PetscSqrtReal(cond2);
9410 
9411     stats.min = PetscMin(stats.min, cond);
9412     stats.max = PetscMax(stats.max, cond);
9413     stats.sum += cond;
9414     stats.squaresum += cond2;
9415     stats.count++;
9416     if (output && cond > limit) {
9417       PetscSection coordSection;
9418       Vec          coordsLocal;
9419       PetscScalar *coords = NULL;
9420       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9421 
9422       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9423       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9424       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9425       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9426       for (i = 0; i < Nv / cdim; ++i) {
9427         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9428         for (d = 0; d < cdim; ++d) {
9429           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9430           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9431         }
9432         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9433       }
9434       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9435       for (cl = 0; cl < clSize * 2; cl += 2) {
9436         const PetscInt edge = closure[cl];
9437 
9438         if ((edge >= eStart) && (edge < eEnd)) {
9439           PetscReal len;
9440 
9441           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9442           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9443         }
9444       }
9445       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9446       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9447     }
9448   }
9449   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9450 
9451   if (size > 1) {
9452     PetscMPIInt  blockLengths[2] = {4, 1};
9453     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9454     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9455     MPI_Op       statReduce;
9456 
9457     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9458     PetscCallMPI(MPI_Type_commit(&statType));
9459     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9460     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9461     PetscCallMPI(MPI_Op_free(&statReduce));
9462     PetscCallMPI(MPI_Type_free(&statType));
9463   } else {
9464     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9465   }
9466   if (rank == 0) {
9467     count = globalStats.count;
9468     min   = globalStats.min;
9469     max   = globalStats.max;
9470     mean  = globalStats.sum / globalStats.count;
9471     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9472   }
9473 
9474   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));
9475   PetscCall(PetscFree2(J, invJ));
9476 
9477   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9478   if (dmCoarse) {
9479     PetscBool isplex;
9480 
9481     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9482     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9483   }
9484   PetscFunctionReturn(PETSC_SUCCESS);
9485 }
9486 
9487 /*@
9488   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9489   orthogonal quality below given tolerance.
9490 
9491   Collective
9492 
9493   Input Parameters:
9494 + dm   - The `DMPLEX` object
9495 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9496 - atol - [0, 1] Absolute tolerance for tagging cells.
9497 
9498   Output Parameters:
9499 + OrthQual      - `Vec` containing orthogonal quality per cell
9500 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9501 
9502   Options Database Keys:
9503 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9504 - -dm_plex_orthogonal_quality_vec_view   - view OrthQual vector.
9505 
9506   Level: intermediate
9507 
9508   Notes:
9509   Orthogonal quality is given by the following formula\:
9510 
9511   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9512 
9513   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
9514   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9515   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9516   calculating the cosine of the angle between these vectors.
9517 
9518   Orthogonal quality ranges from 1 (best) to 0 (worst).
9519 
9520   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9521   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9522 
9523   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9524 
9525 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9526 @*/
9527 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9528 {
9529   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9530   PetscInt              *idx;
9531   PetscScalar           *oqVals;
9532   const PetscScalar     *cellGeomArr, *faceGeomArr;
9533   PetscReal             *ci, *fi, *Ai;
9534   MPI_Comm               comm;
9535   Vec                    cellgeom, facegeom;
9536   DM                     dmFace, dmCell;
9537   IS                     glob;
9538   ISLocalToGlobalMapping ltog;
9539   PetscViewer            vwr;
9540 
9541   PetscFunctionBegin;
9542   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9543   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9544   PetscAssertPointer(OrthQual, 4);
9545   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9546   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9547   PetscCall(DMGetDimension(dm, &nc));
9548   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9549   {
9550     DMPlexInterpolatedFlag interpFlag;
9551 
9552     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9553     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9554       PetscMPIInt rank;
9555 
9556       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9557       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9558     }
9559   }
9560   if (OrthQualLabel) {
9561     PetscAssertPointer(OrthQualLabel, 5);
9562     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9563     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9564   } else {
9565     *OrthQualLabel = NULL;
9566   }
9567   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9568   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9569   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
9570   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9571   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9572   PetscCall(VecCreate(comm, OrthQual));
9573   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9574   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9575   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9576   PetscCall(VecSetUp(*OrthQual));
9577   PetscCall(ISDestroy(&glob));
9578   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9579   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9580   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9581   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9582   PetscCall(VecGetDM(cellgeom, &dmCell));
9583   PetscCall(VecGetDM(facegeom, &dmFace));
9584   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9585   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
9586     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9587     PetscInt         cellarr[2], *adj = NULL;
9588     PetscScalar     *cArr, *fArr;
9589     PetscReal        minvalc = 1.0, minvalf = 1.0;
9590     PetscFVCellGeom *cg;
9591 
9592     idx[cellIter] = cell - cStart;
9593     cellarr[0]    = cell;
9594     /* Make indexing into cellGeom easier */
9595     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9596     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9597     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9598     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9599     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
9600       PetscInt         i;
9601       const PetscInt   neigh  = adj[cellneigh];
9602       PetscReal        normci = 0, normfi = 0, normai = 0;
9603       PetscFVCellGeom *cgneigh;
9604       PetscFVFaceGeom *fg;
9605 
9606       /* Don't count ourselves in the neighbor list */
9607       if (neigh == cell) continue;
9608       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9609       cellarr[1] = neigh;
9610       {
9611         PetscInt        numcovpts;
9612         const PetscInt *covpts;
9613 
9614         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9615         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9616         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9617       }
9618 
9619       /* Compute c_i, f_i and their norms */
9620       for (i = 0; i < nc; i++) {
9621         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9622         fi[i] = fg->centroid[i] - cg->centroid[i];
9623         Ai[i] = fg->normal[i];
9624         normci += PetscPowReal(ci[i], 2);
9625         normfi += PetscPowReal(fi[i], 2);
9626         normai += PetscPowReal(Ai[i], 2);
9627       }
9628       normci = PetscSqrtReal(normci);
9629       normfi = PetscSqrtReal(normfi);
9630       normai = PetscSqrtReal(normai);
9631 
9632       /* Normalize and compute for each face-cell-normal pair */
9633       for (i = 0; i < nc; i++) {
9634         ci[i] = ci[i] / normci;
9635         fi[i] = fi[i] / normfi;
9636         Ai[i] = Ai[i] / normai;
9637         /* PetscAbs because I don't know if normals are guaranteed to point out */
9638         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
9639         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
9640       }
9641       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
9642       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
9643     }
9644     PetscCall(PetscFree(adj));
9645     PetscCall(PetscFree2(cArr, fArr));
9646     /* Defer to cell if they're equal */
9647     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9648     if (OrthQualLabel) {
9649       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9650     }
9651   }
9652   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
9653   PetscCall(VecAssemblyBegin(*OrthQual));
9654   PetscCall(VecAssemblyEnd(*OrthQual));
9655   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9656   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9657   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9658   if (OrthQualLabel) {
9659     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9660   }
9661   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9662   PetscCall(PetscViewerDestroy(&vwr));
9663   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9664   PetscFunctionReturn(PETSC_SUCCESS);
9665 }
9666 
9667 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9668  * interpolator construction */
9669 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9670 {
9671   PetscSection section, newSection, gsection;
9672   PetscSF      sf;
9673   PetscBool    hasConstraints, ghasConstraints;
9674 
9675   PetscFunctionBegin;
9676   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9677   PetscAssertPointer(odm, 2);
9678   PetscCall(DMGetLocalSection(dm, &section));
9679   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9680   PetscCall(MPIU_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
9681   if (!ghasConstraints) {
9682     PetscCall(PetscObjectReference((PetscObject)dm));
9683     *odm = dm;
9684     PetscFunctionReturn(PETSC_SUCCESS);
9685   }
9686   PetscCall(DMClone(dm, odm));
9687   PetscCall(DMCopyFields(dm, *odm));
9688   PetscCall(DMGetLocalSection(*odm, &newSection));
9689   PetscCall(DMGetPointSF(*odm, &sf));
9690   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_FALSE, &gsection));
9691   PetscCall(DMSetGlobalSection(*odm, gsection));
9692   PetscCall(PetscSectionDestroy(&gsection));
9693   PetscFunctionReturn(PETSC_SUCCESS);
9694 }
9695 
9696 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9697 {
9698   DM        dmco, dmfo;
9699   Mat       interpo;
9700   Vec       rscale;
9701   Vec       cglobalo, clocal;
9702   Vec       fglobal, fglobalo, flocal;
9703   PetscBool regular;
9704 
9705   PetscFunctionBegin;
9706   PetscCall(DMGetFullDM(dmc, &dmco));
9707   PetscCall(DMGetFullDM(dmf, &dmfo));
9708   PetscCall(DMSetCoarseDM(dmfo, dmco));
9709   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9710   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9711   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9712   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9713   PetscCall(DMCreateLocalVector(dmc, &clocal));
9714   PetscCall(VecSet(cglobalo, 0.));
9715   PetscCall(VecSet(clocal, 0.));
9716   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9717   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9718   PetscCall(DMCreateLocalVector(dmf, &flocal));
9719   PetscCall(VecSet(fglobal, 0.));
9720   PetscCall(VecSet(fglobalo, 0.));
9721   PetscCall(VecSet(flocal, 0.));
9722   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9723   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9724   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9725   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9726   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9727   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9728   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9729   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9730   *shift = fglobal;
9731   PetscCall(VecDestroy(&flocal));
9732   PetscCall(VecDestroy(&fglobalo));
9733   PetscCall(VecDestroy(&clocal));
9734   PetscCall(VecDestroy(&cglobalo));
9735   PetscCall(VecDestroy(&rscale));
9736   PetscCall(MatDestroy(&interpo));
9737   PetscCall(DMDestroy(&dmfo));
9738   PetscCall(DMDestroy(&dmco));
9739   PetscFunctionReturn(PETSC_SUCCESS);
9740 }
9741 
9742 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
9743 {
9744   PetscObject shifto;
9745   Vec         shift;
9746 
9747   PetscFunctionBegin;
9748   if (!interp) {
9749     Vec rscale;
9750 
9751     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9752     PetscCall(VecDestroy(&rscale));
9753   } else {
9754     PetscCall(PetscObjectReference((PetscObject)interp));
9755   }
9756   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9757   if (!shifto) {
9758     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9759     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
9760     shifto = (PetscObject)shift;
9761     PetscCall(VecDestroy(&shift));
9762   }
9763   shift = (Vec)shifto;
9764   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9765   PetscCall(VecAXPY(fineSol, 1.0, shift));
9766   PetscCall(MatDestroy(&interp));
9767   PetscFunctionReturn(PETSC_SUCCESS);
9768 }
9769 
9770 /* Pointwise interpolation
9771      Just code FEM for now
9772      u^f = I u^c
9773      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9774      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9775      I_{ij} = psi^f_i phi^c_j
9776 */
9777 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
9778 {
9779   PetscSection gsc, gsf;
9780   PetscInt     m, n;
9781   void        *ctx;
9782   DM           cdm;
9783   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9784 
9785   PetscFunctionBegin;
9786   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9787   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9788   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9789   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9790 
9791   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9792   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
9793   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9794   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9795   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9796 
9797   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9798   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9799   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9800   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9801   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9802   if (scaling) {
9803     /* Use naive scaling */
9804     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
9805   }
9806   PetscFunctionReturn(PETSC_SUCCESS);
9807 }
9808 
9809 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
9810 {
9811   VecScatter ctx;
9812 
9813   PetscFunctionBegin;
9814   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
9815   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
9816   PetscCall(VecScatterDestroy(&ctx));
9817   PetscFunctionReturn(PETSC_SUCCESS);
9818 }
9819 
9820 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[])
9821 {
9822   const PetscInt Nc = uOff[1] - uOff[0];
9823   PetscInt       c;
9824   for (c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
9825 }
9826 
9827 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
9828 {
9829   DM           dmc;
9830   PetscDS      ds;
9831   Vec          ones, locmass;
9832   IS           cellIS;
9833   PetscFormKey key;
9834   PetscInt     depth;
9835 
9836   PetscFunctionBegin;
9837   PetscCall(DMClone(dm, &dmc));
9838   PetscCall(DMCopyDisc(dm, dmc));
9839   PetscCall(DMGetDS(dmc, &ds));
9840   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9841   PetscCall(DMCreateGlobalVector(dmc, mass));
9842   PetscCall(DMGetLocalVector(dmc, &ones));
9843   PetscCall(DMGetLocalVector(dmc, &locmass));
9844   PetscCall(DMPlexGetDepth(dmc, &depth));
9845   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9846   PetscCall(VecSet(locmass, 0.0));
9847   PetscCall(VecSet(ones, 1.0));
9848   key.label = NULL;
9849   key.value = 0;
9850   key.field = 0;
9851   key.part  = 0;
9852   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
9853   PetscCall(ISDestroy(&cellIS));
9854   PetscCall(VecSet(*mass, 0.0));
9855   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
9856   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
9857   PetscCall(DMRestoreLocalVector(dmc, &ones));
9858   PetscCall(DMRestoreLocalVector(dmc, &locmass));
9859   PetscCall(DMDestroy(&dmc));
9860   PetscFunctionReturn(PETSC_SUCCESS);
9861 }
9862 
9863 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
9864 {
9865   PetscSection gsc, gsf;
9866   PetscInt     m, n;
9867   void        *ctx;
9868   DM           cdm;
9869   PetscBool    regular;
9870 
9871   PetscFunctionBegin;
9872   if (dmFine == dmCoarse) {
9873     DM            dmc;
9874     PetscDS       ds;
9875     PetscWeakForm wf;
9876     Vec           u;
9877     IS            cellIS;
9878     PetscFormKey  key;
9879     PetscInt      depth;
9880 
9881     PetscCall(DMClone(dmFine, &dmc));
9882     PetscCall(DMCopyDisc(dmFine, dmc));
9883     PetscCall(DMGetDS(dmc, &ds));
9884     PetscCall(PetscDSGetWeakForm(ds, &wf));
9885     PetscCall(PetscWeakFormClear(wf));
9886     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9887     PetscCall(DMCreateMatrix(dmc, mass));
9888     PetscCall(DMGetLocalVector(dmc, &u));
9889     PetscCall(DMPlexGetDepth(dmc, &depth));
9890     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9891     PetscCall(MatZeroEntries(*mass));
9892     key.label = NULL;
9893     key.value = 0;
9894     key.field = 0;
9895     key.part  = 0;
9896     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
9897     PetscCall(ISDestroy(&cellIS));
9898     PetscCall(DMRestoreLocalVector(dmc, &u));
9899     PetscCall(DMDestroy(&dmc));
9900   } else {
9901     PetscCall(DMGetGlobalSection(dmFine, &gsf));
9902     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9903     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9904     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9905 
9906     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
9907     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9908     PetscCall(MatSetType(*mass, dmCoarse->mattype));
9909     PetscCall(DMGetApplicationContext(dmFine, &ctx));
9910 
9911     PetscCall(DMGetCoarseDM(dmFine, &cdm));
9912     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9913     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
9914     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
9915   }
9916   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
9917   PetscFunctionReturn(PETSC_SUCCESS);
9918 }
9919 
9920 /*@
9921   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9922 
9923   Input Parameter:
9924 . dm - The `DMPLEX` object
9925 
9926   Output Parameter:
9927 . regular - The flag
9928 
9929   Level: intermediate
9930 
9931 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
9932 @*/
9933 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
9934 {
9935   PetscFunctionBegin;
9936   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9937   PetscAssertPointer(regular, 2);
9938   *regular = ((DM_Plex *)dm->data)->regularRefinement;
9939   PetscFunctionReturn(PETSC_SUCCESS);
9940 }
9941 
9942 /*@
9943   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9944 
9945   Input Parameters:
9946 + dm      - The `DMPLEX` object
9947 - regular - The flag
9948 
9949   Level: intermediate
9950 
9951 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
9952 @*/
9953 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
9954 {
9955   PetscFunctionBegin;
9956   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9957   ((DM_Plex *)dm->data)->regularRefinement = regular;
9958   PetscFunctionReturn(PETSC_SUCCESS);
9959 }
9960 
9961 /*@
9962   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
9963   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
9964 
9965   Not Collective
9966 
9967   Input Parameter:
9968 . dm - The `DMPLEX` object
9969 
9970   Output Parameters:
9971 + anchorSection - If not `NULL`, set to the section describing which points anchor the constrained points.
9972 - anchorIS      - If not `NULL`, set to the list of anchors indexed by `anchorSection`
9973 
9974   Level: intermediate
9975 
9976 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
9977 @*/
9978 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
9979 {
9980   DM_Plex *plex = (DM_Plex *)dm->data;
9981 
9982   PetscFunctionBegin;
9983   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9984   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
9985   if (anchorSection) *anchorSection = plex->anchorSection;
9986   if (anchorIS) *anchorIS = plex->anchorIS;
9987   PetscFunctionReturn(PETSC_SUCCESS);
9988 }
9989 
9990 /*@
9991   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.
9992 
9993   Collective
9994 
9995   Input Parameters:
9996 + dm            - The `DMPLEX` object
9997 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
9998                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
9999 - anchorIS      - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10000 
10001   Level: intermediate
10002 
10003   Notes:
10004   Unlike boundary conditions, when a point's degrees of freedom in a section are constrained to
10005   an outside value, the anchor constraints set a point's degrees of freedom to be a linear
10006   combination of other points' degrees of freedom.
10007 
10008   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
10009   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
10010 
10011   The reference counts of `anchorSection` and `anchorIS` are incremented.
10012 
10013 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
10014 @*/
10015 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
10016 {
10017   DM_Plex    *plex = (DM_Plex *)dm->data;
10018   PetscMPIInt result;
10019 
10020   PetscFunctionBegin;
10021   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10022   if (anchorSection) {
10023     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
10024     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
10025     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
10026   }
10027   if (anchorIS) {
10028     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
10029     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
10030     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
10031   }
10032 
10033   PetscCall(PetscObjectReference((PetscObject)anchorSection));
10034   PetscCall(PetscSectionDestroy(&plex->anchorSection));
10035   plex->anchorSection = anchorSection;
10036 
10037   PetscCall(PetscObjectReference((PetscObject)anchorIS));
10038   PetscCall(ISDestroy(&plex->anchorIS));
10039   plex->anchorIS = anchorIS;
10040 
10041   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
10042     PetscInt        size, a, pStart, pEnd;
10043     const PetscInt *anchors;
10044 
10045     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10046     PetscCall(ISGetLocalSize(anchorIS, &size));
10047     PetscCall(ISGetIndices(anchorIS, &anchors));
10048     for (a = 0; a < size; a++) {
10049       PetscInt p;
10050 
10051       p = anchors[a];
10052       if (p >= pStart && p < pEnd) {
10053         PetscInt dof;
10054 
10055         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10056         if (dof) {
10057           PetscCall(ISRestoreIndices(anchorIS, &anchors));
10058           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
10059         }
10060       }
10061     }
10062     PetscCall(ISRestoreIndices(anchorIS, &anchors));
10063   }
10064   /* reset the generic constraints */
10065   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
10066   PetscFunctionReturn(PETSC_SUCCESS);
10067 }
10068 
10069 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
10070 {
10071   PetscSection anchorSection;
10072   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
10073 
10074   PetscFunctionBegin;
10075   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10076   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10077   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
10078   PetscCall(PetscSectionGetNumFields(section, &numFields));
10079   if (numFields) {
10080     PetscInt f;
10081     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
10082 
10083     for (f = 0; f < numFields; f++) {
10084       PetscInt numComp;
10085 
10086       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
10087       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
10088     }
10089   }
10090   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10091   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10092   pStart = PetscMax(pStart, sStart);
10093   pEnd   = PetscMin(pEnd, sEnd);
10094   pEnd   = PetscMax(pStart, pEnd);
10095   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
10096   for (p = pStart; p < pEnd; p++) {
10097     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10098     if (dof) {
10099       PetscCall(PetscSectionGetDof(section, p, &dof));
10100       PetscCall(PetscSectionSetDof(*cSec, p, dof));
10101       for (f = 0; f < numFields; f++) {
10102         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
10103         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
10104       }
10105     }
10106   }
10107   PetscCall(PetscSectionSetUp(*cSec));
10108   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
10109   PetscFunctionReturn(PETSC_SUCCESS);
10110 }
10111 
10112 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
10113 {
10114   PetscSection    aSec;
10115   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
10116   const PetscInt *anchors;
10117   PetscInt        numFields, f;
10118   IS              aIS;
10119   MatType         mtype;
10120   PetscBool       iscuda, iskokkos;
10121 
10122   PetscFunctionBegin;
10123   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10124   PetscCall(PetscSectionGetStorageSize(cSec, &m));
10125   PetscCall(PetscSectionGetStorageSize(section, &n));
10126   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
10127   PetscCall(MatSetSizes(*cMat, m, n, m, n));
10128   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
10129   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
10130   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
10131   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
10132   if (iscuda) mtype = MATSEQAIJCUSPARSE;
10133   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
10134   else mtype = MATSEQAIJ;
10135   PetscCall(MatSetType(*cMat, mtype));
10136   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
10137   PetscCall(ISGetIndices(aIS, &anchors));
10138   /* cSec will be a subset of aSec and section */
10139   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
10140   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10141   PetscCall(PetscMalloc1(m + 1, &i));
10142   i[0] = 0;
10143   PetscCall(PetscSectionGetNumFields(section, &numFields));
10144   for (p = pStart; p < pEnd; p++) {
10145     PetscInt rDof, rOff, r;
10146 
10147     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10148     if (!rDof) continue;
10149     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10150     if (numFields) {
10151       for (f = 0; f < numFields; f++) {
10152         annz = 0;
10153         for (r = 0; r < rDof; r++) {
10154           a = anchors[rOff + r];
10155           if (a < sStart || a >= sEnd) continue;
10156           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10157           annz += aDof;
10158         }
10159         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10160         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
10161         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10162       }
10163     } else {
10164       annz = 0;
10165       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10166       for (q = 0; q < dof; q++) {
10167         a = anchors[rOff + q];
10168         if (a < sStart || a >= sEnd) continue;
10169         PetscCall(PetscSectionGetDof(section, a, &aDof));
10170         annz += aDof;
10171       }
10172       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10173       PetscCall(PetscSectionGetOffset(cSec, p, &off));
10174       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10175     }
10176   }
10177   nnz = i[m];
10178   PetscCall(PetscMalloc1(nnz, &j));
10179   offset = 0;
10180   for (p = pStart; p < pEnd; p++) {
10181     if (numFields) {
10182       for (f = 0; f < numFields; f++) {
10183         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10184         for (q = 0; q < dof; q++) {
10185           PetscInt rDof, rOff, r;
10186           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10187           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10188           for (r = 0; r < rDof; r++) {
10189             PetscInt s;
10190 
10191             a = anchors[rOff + r];
10192             if (a < sStart || a >= sEnd) continue;
10193             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10194             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
10195             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10196           }
10197         }
10198       }
10199     } else {
10200       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10201       for (q = 0; q < dof; q++) {
10202         PetscInt rDof, rOff, r;
10203         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10204         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10205         for (r = 0; r < rDof; r++) {
10206           PetscInt s;
10207 
10208           a = anchors[rOff + r];
10209           if (a < sStart || a >= sEnd) continue;
10210           PetscCall(PetscSectionGetDof(section, a, &aDof));
10211           PetscCall(PetscSectionGetOffset(section, a, &aOff));
10212           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10213         }
10214       }
10215     }
10216   }
10217   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
10218   PetscCall(PetscFree(i));
10219   PetscCall(PetscFree(j));
10220   PetscCall(ISRestoreIndices(aIS, &anchors));
10221   PetscFunctionReturn(PETSC_SUCCESS);
10222 }
10223 
10224 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
10225 {
10226   DM_Plex     *plex = (DM_Plex *)dm->data;
10227   PetscSection anchorSection, section, cSec;
10228   Mat          cMat;
10229 
10230   PetscFunctionBegin;
10231   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10232   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10233   if (anchorSection) {
10234     PetscInt Nf;
10235 
10236     PetscCall(DMGetLocalSection(dm, &section));
10237     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
10238     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
10239     PetscCall(DMGetNumFields(dm, &Nf));
10240     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
10241     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
10242     PetscCall(PetscSectionDestroy(&cSec));
10243     PetscCall(MatDestroy(&cMat));
10244   }
10245   PetscFunctionReturn(PETSC_SUCCESS);
10246 }
10247 
10248 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10249 {
10250   IS           subis;
10251   PetscSection section, subsection;
10252 
10253   PetscFunctionBegin;
10254   PetscCall(DMGetLocalSection(dm, &section));
10255   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10256   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10257   /* Create subdomain */
10258   PetscCall(DMPlexFilter(dm, label, value, subdm));
10259   /* Create submodel */
10260   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10261   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10262   PetscCall(DMSetLocalSection(*subdm, subsection));
10263   PetscCall(PetscSectionDestroy(&subsection));
10264   PetscCall(DMCopyDisc(dm, *subdm));
10265   /* Create map from submodel to global model */
10266   if (is) {
10267     PetscSection    sectionGlobal, subsectionGlobal;
10268     IS              spIS;
10269     const PetscInt *spmap;
10270     PetscInt       *subIndices;
10271     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10272     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10273 
10274     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10275     PetscCall(ISGetIndices(spIS, &spmap));
10276     PetscCall(PetscSectionGetNumFields(section, &Nf));
10277     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10278     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10279     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10280     for (p = pStart; p < pEnd; ++p) {
10281       PetscInt gdof, pSubSize = 0;
10282 
10283       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10284       if (gdof > 0) {
10285         for (f = 0; f < Nf; ++f) {
10286           PetscInt fdof, fcdof;
10287 
10288           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10289           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10290           pSubSize += fdof - fcdof;
10291         }
10292         subSize += pSubSize;
10293         if (pSubSize) {
10294           if (bs < 0) {
10295             bs = pSubSize;
10296           } else if (bs != pSubSize) {
10297             /* Layout does not admit a pointwise block size */
10298             bs = 1;
10299           }
10300         }
10301       }
10302     }
10303     /* Must have same blocksize on all procs (some might have no points) */
10304     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
10305     bsLocal[1] = bs;
10306     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10307     if (bsMinMax[0] != bsMinMax[1]) {
10308       bs = 1;
10309     } else {
10310       bs = bsMinMax[0];
10311     }
10312     PetscCall(PetscMalloc1(subSize, &subIndices));
10313     for (p = pStart; p < pEnd; ++p) {
10314       PetscInt gdof, goff;
10315 
10316       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10317       if (gdof > 0) {
10318         const PetscInt point = spmap[p];
10319 
10320         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10321         for (f = 0; f < Nf; ++f) {
10322           PetscInt fdof, fcdof, fc, f2, poff = 0;
10323 
10324           /* Can get rid of this loop by storing field information in the global section */
10325           for (f2 = 0; f2 < f; ++f2) {
10326             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10327             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10328             poff += fdof - fcdof;
10329           }
10330           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10331           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10332           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10333         }
10334       }
10335     }
10336     PetscCall(ISRestoreIndices(spIS, &spmap));
10337     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10338     if (bs > 1) {
10339       /* We need to check that the block size does not come from non-contiguous fields */
10340       PetscInt i, j, set = 1;
10341       for (i = 0; i < subSize; i += bs) {
10342         for (j = 0; j < bs; ++j) {
10343           if (subIndices[i + j] != subIndices[i] + j) {
10344             set = 0;
10345             break;
10346           }
10347         }
10348       }
10349       if (set) PetscCall(ISSetBlockSize(*is, bs));
10350     }
10351     /* Attach nullspace */
10352     for (f = 0; f < Nf; ++f) {
10353       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10354       if ((*subdm)->nullspaceConstructors[f]) break;
10355     }
10356     if (f < Nf) {
10357       MatNullSpace nullSpace;
10358       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10359 
10360       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10361       PetscCall(MatNullSpaceDestroy(&nullSpace));
10362     }
10363   }
10364   PetscFunctionReturn(PETSC_SUCCESS);
10365 }
10366 
10367 /*@
10368   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10369 
10370   Input Parameters:
10371 + dm    - The `DM`
10372 - dummy - unused argument
10373 
10374   Options Database Key:
10375 . -dm_plex_monitor_throughput - Activate the monitor
10376 
10377   Level: developer
10378 
10379 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10380 @*/
10381 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10382 {
10383   PetscLogHandler default_handler;
10384 
10385   PetscFunctionBegin;
10386   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10387   PetscCall(PetscLogGetDefaultHandler(&default_handler));
10388   if (default_handler) {
10389     PetscLogEvent      event;
10390     PetscEventPerfInfo eventInfo;
10391     PetscReal          cellRate, flopRate;
10392     PetscInt           cStart, cEnd, Nf, N;
10393     const char        *name;
10394 
10395     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10396     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10397     PetscCall(DMGetNumFields(dm, &Nf));
10398     PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10399     PetscCall(PetscLogEventGetPerfInfo(PETSC_DEFAULT, event, &eventInfo));
10400     N        = (cEnd - cStart) * Nf * eventInfo.count;
10401     flopRate = eventInfo.flops / eventInfo.time;
10402     cellRate = N / eventInfo.time;
10403     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)));
10404   } else {
10405     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.");
10406   }
10407   PetscFunctionReturn(PETSC_SUCCESS);
10408 }
10409