xref: /petsc/src/dm/impls/plex/plex.c (revision 4936d80934666c0fe36796ce94b63b57bfb10049)
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 #if defined(PETSC_HAVE_EXODUSII)
1852   #include <exodusII.h>
1853   #include <petscviewerexodusii.h>
1854 #endif
1855 
1856 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1857 {
1858   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns;
1859   char      name[PETSC_MAX_PATH_LEN];
1860 
1861   PetscFunctionBegin;
1862   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1863   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1864   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
1865   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
1866   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1867   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
1868   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
1869   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
1870   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
1871   if (iascii) {
1872     PetscViewerFormat format;
1873     PetscCall(PetscViewerGetFormat(viewer, &format));
1874     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1875     else PetscCall(DMPlexView_Ascii(dm, viewer));
1876   } else if (ishdf5) {
1877 #if defined(PETSC_HAVE_HDF5)
1878     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1879 #else
1880     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1881 #endif
1882   } else if (isvtk) {
1883     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
1884   } else if (isdraw) {
1885     PetscCall(DMPlexView_Draw(dm, viewer));
1886   } else if (isglvis) {
1887     PetscCall(DMPlexView_GLVis(dm, viewer));
1888 #if defined(PETSC_HAVE_EXODUSII)
1889   } else if (isexodus) {
1890     /*
1891       exodusII requires that all sets be part of exactly one cell set.
1892       If the dm does not have a "Cell Sets" label defined, we create one
1893       with ID 1, containing all cells.
1894       Note that if the Cell Sets label is defined but does not cover all cells,
1895       we may still have a problem. This should probably be checked here or in the viewer;
1896     */
1897     PetscInt numCS;
1898     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
1899     if (!numCS) {
1900       PetscInt cStart, cEnd, c;
1901       PetscCall(DMCreateLabel(dm, "Cell Sets"));
1902       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1903       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
1904     }
1905     PetscCall(DMView_PlexExodusII(dm, viewer));
1906 #endif
1907 #if defined(PETSC_HAVE_CGNS)
1908   } else if (iscgns) {
1909     PetscCall(DMView_PlexCGNS(dm, viewer));
1910 #endif
1911   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
1912   /* Optionally view the partition */
1913   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
1914   if (flg) {
1915     Vec ranks;
1916     PetscCall(DMPlexCreateRankField(dm, &ranks));
1917     PetscCall(VecView(ranks, viewer));
1918     PetscCall(VecDestroy(&ranks));
1919   }
1920   /* Optionally view a label */
1921   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
1922   if (flg) {
1923     DMLabel label;
1924     Vec     val;
1925 
1926     PetscCall(DMGetLabel(dm, name, &label));
1927     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
1928     PetscCall(DMPlexCreateLabelField(dm, label, &val));
1929     PetscCall(VecView(val, viewer));
1930     PetscCall(VecDestroy(&val));
1931   }
1932   PetscFunctionReturn(PETSC_SUCCESS);
1933 }
1934 
1935 /*@
1936   DMPlexTopologyView - Saves a `DMPLEX` topology into a file
1937 
1938   Collective
1939 
1940   Input Parameters:
1941 + dm     - The `DM` whose topology is to be saved
1942 - viewer - The `PetscViewer` to save it in
1943 
1944   Level: advanced
1945 
1946 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`, `PetscViewer`
1947 @*/
1948 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
1949 {
1950   PetscBool ishdf5;
1951 
1952   PetscFunctionBegin;
1953   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1954   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1955   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1956   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
1957   if (ishdf5) {
1958 #if defined(PETSC_HAVE_HDF5)
1959     PetscViewerFormat format;
1960     PetscCall(PetscViewerGetFormat(viewer, &format));
1961     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1962       IS globalPointNumbering;
1963 
1964       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1965       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
1966       PetscCall(ISDestroy(&globalPointNumbering));
1967     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
1968 #else
1969     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1970 #endif
1971   }
1972   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
1973   PetscFunctionReturn(PETSC_SUCCESS);
1974 }
1975 
1976 /*@
1977   DMPlexCoordinatesView - Saves `DMPLEX` coordinates into a file
1978 
1979   Collective
1980 
1981   Input Parameters:
1982 + dm     - The `DM` whose coordinates are to be saved
1983 - viewer - The `PetscViewer` for saving
1984 
1985   Level: advanced
1986 
1987 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`, `PetscViewer`
1988 @*/
1989 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
1990 {
1991   PetscBool ishdf5;
1992 
1993   PetscFunctionBegin;
1994   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1995   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1996   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1997   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
1998   if (ishdf5) {
1999 #if defined(PETSC_HAVE_HDF5)
2000     PetscViewerFormat format;
2001     PetscCall(PetscViewerGetFormat(viewer, &format));
2002     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2003       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
2004     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2005 #else
2006     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2007 #endif
2008   }
2009   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2010   PetscFunctionReturn(PETSC_SUCCESS);
2011 }
2012 
2013 /*@
2014   DMPlexLabelsView - Saves `DMPLEX` labels into a file
2015 
2016   Collective
2017 
2018   Input Parameters:
2019 + dm     - The `DM` whose labels are to be saved
2020 - viewer - The `PetscViewer` for saving
2021 
2022   Level: advanced
2023 
2024 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`, `PetscViewer`
2025 @*/
2026 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
2027 {
2028   PetscBool ishdf5;
2029 
2030   PetscFunctionBegin;
2031   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2032   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2033   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2034   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
2035   if (ishdf5) {
2036 #if defined(PETSC_HAVE_HDF5)
2037     IS                globalPointNumbering;
2038     PetscViewerFormat format;
2039 
2040     PetscCall(PetscViewerGetFormat(viewer, &format));
2041     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2042       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2043       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
2044       PetscCall(ISDestroy(&globalPointNumbering));
2045     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2046 #else
2047     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2048 #endif
2049   }
2050   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
2051   PetscFunctionReturn(PETSC_SUCCESS);
2052 }
2053 
2054 /*@
2055   DMPlexSectionView - Saves a section associated with a `DMPLEX`
2056 
2057   Collective
2058 
2059   Input Parameters:
2060 + dm        - The `DM` that contains the topology on which the section to be saved is defined
2061 . viewer    - The `PetscViewer` for saving
2062 - sectiondm - The `DM` that contains the section to be saved
2063 
2064   Level: advanced
2065 
2066   Notes:
2067   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.
2068 
2069   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.
2070 
2071 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`, `PetscViewer`
2072 @*/
2073 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
2074 {
2075   PetscBool ishdf5;
2076 
2077   PetscFunctionBegin;
2078   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2079   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2080   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2081   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2082   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
2083   if (ishdf5) {
2084 #if defined(PETSC_HAVE_HDF5)
2085     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
2086 #else
2087     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2088 #endif
2089   }
2090   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
2091   PetscFunctionReturn(PETSC_SUCCESS);
2092 }
2093 
2094 /*@
2095   DMPlexGlobalVectorView - Saves a global vector
2096 
2097   Collective
2098 
2099   Input Parameters:
2100 + dm        - The `DM` that represents the topology
2101 . viewer    - The `PetscViewer` to save data with
2102 . sectiondm - The `DM` that contains the global section on which vec is defined
2103 - vec       - The global vector to be saved
2104 
2105   Level: advanced
2106 
2107   Notes:
2108   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.
2109 
2110   Calling sequence:
2111 .vb
2112        DMCreate(PETSC_COMM_WORLD, &dm);
2113        DMSetType(dm, DMPLEX);
2114        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2115        DMClone(dm, &sectiondm);
2116        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2117        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2118        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2119        PetscSectionSetChart(section, pStart, pEnd);
2120        PetscSectionSetUp(section);
2121        DMSetLocalSection(sectiondm, section);
2122        PetscSectionDestroy(&section);
2123        DMGetGlobalVector(sectiondm, &vec);
2124        PetscObjectSetName((PetscObject)vec, "vec_name");
2125        DMPlexTopologyView(dm, viewer);
2126        DMPlexSectionView(dm, viewer, sectiondm);
2127        DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
2128        DMRestoreGlobalVector(sectiondm, &vec);
2129        DMDestroy(&sectiondm);
2130        DMDestroy(&dm);
2131 .ve
2132 
2133 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2134 @*/
2135 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2136 {
2137   PetscBool ishdf5;
2138 
2139   PetscFunctionBegin;
2140   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2141   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2142   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2143   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2144   /* Check consistency */
2145   {
2146     PetscSection section;
2147     PetscBool    includesConstraints;
2148     PetscInt     m, m1;
2149 
2150     PetscCall(VecGetLocalSize(vec, &m1));
2151     PetscCall(DMGetGlobalSection(sectiondm, &section));
2152     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2153     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2154     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2155     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2156   }
2157   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2158   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2159   if (ishdf5) {
2160 #if defined(PETSC_HAVE_HDF5)
2161     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2162 #else
2163     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2164 #endif
2165   }
2166   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2167   PetscFunctionReturn(PETSC_SUCCESS);
2168 }
2169 
2170 /*@
2171   DMPlexLocalVectorView - Saves a local vector
2172 
2173   Collective
2174 
2175   Input Parameters:
2176 + dm        - The `DM` that represents the topology
2177 . viewer    - The `PetscViewer` to save data with
2178 . sectiondm - The `DM` that contains the local section on which `vec` is defined; may be the same as `dm`
2179 - vec       - The local vector to be saved
2180 
2181   Level: advanced
2182 
2183   Note:
2184   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.
2185 
2186   Calling sequence:
2187 .vb
2188        DMCreate(PETSC_COMM_WORLD, &dm);
2189        DMSetType(dm, DMPLEX);
2190        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2191        DMClone(dm, &sectiondm);
2192        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2193        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2194        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2195        PetscSectionSetChart(section, pStart, pEnd);
2196        PetscSectionSetUp(section);
2197        DMSetLocalSection(sectiondm, section);
2198        DMGetLocalVector(sectiondm, &vec);
2199        PetscObjectSetName((PetscObject)vec, "vec_name");
2200        DMPlexTopologyView(dm, viewer);
2201        DMPlexSectionView(dm, viewer, sectiondm);
2202        DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2203        DMRestoreLocalVector(sectiondm, &vec);
2204        DMDestroy(&sectiondm);
2205        DMDestroy(&dm);
2206 .ve
2207 
2208 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2209 @*/
2210 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2211 {
2212   PetscBool ishdf5;
2213 
2214   PetscFunctionBegin;
2215   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2216   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2217   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2218   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2219   /* Check consistency */
2220   {
2221     PetscSection section;
2222     PetscBool    includesConstraints;
2223     PetscInt     m, m1;
2224 
2225     PetscCall(VecGetLocalSize(vec, &m1));
2226     PetscCall(DMGetLocalSection(sectiondm, &section));
2227     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2228     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2229     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2230     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2231   }
2232   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2233   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2234   if (ishdf5) {
2235 #if defined(PETSC_HAVE_HDF5)
2236     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2237 #else
2238     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2239 #endif
2240   }
2241   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2242   PetscFunctionReturn(PETSC_SUCCESS);
2243 }
2244 
2245 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2246 {
2247   PetscBool ishdf5;
2248 
2249   PetscFunctionBegin;
2250   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2251   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2252   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2253   if (ishdf5) {
2254 #if defined(PETSC_HAVE_HDF5)
2255     PetscViewerFormat format;
2256     PetscCall(PetscViewerGetFormat(viewer, &format));
2257     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2258       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2259     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2260       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2261     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2262     PetscFunctionReturn(PETSC_SUCCESS);
2263 #else
2264     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2265 #endif
2266   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2267 }
2268 
2269 /*@
2270   DMPlexTopologyLoad - Loads a topology into a `DMPLEX`
2271 
2272   Collective
2273 
2274   Input Parameters:
2275 + dm     - The `DM` into which the topology is loaded
2276 - viewer - The `PetscViewer` for the saved topology
2277 
2278   Output Parameter:
2279 . 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
2280 
2281   Level: advanced
2282 
2283 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2284           `PetscViewer`, `PetscSF`
2285 @*/
2286 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2287 {
2288   PetscBool ishdf5;
2289 
2290   PetscFunctionBegin;
2291   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2292   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2293   if (globalToLocalPointSF) PetscAssertPointer(globalToLocalPointSF, 3);
2294   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2295   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2296   if (ishdf5) {
2297 #if defined(PETSC_HAVE_HDF5)
2298     PetscViewerFormat format;
2299     PetscCall(PetscViewerGetFormat(viewer, &format));
2300     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2301       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2302     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2303 #else
2304     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2305 #endif
2306   }
2307   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2308   PetscFunctionReturn(PETSC_SUCCESS);
2309 }
2310 
2311 /*@
2312   DMPlexCoordinatesLoad - Loads coordinates into a `DMPLEX`
2313 
2314   Collective
2315 
2316   Input Parameters:
2317 + dm                   - The `DM` into which the coordinates are loaded
2318 . viewer               - The `PetscViewer` for the saved coordinates
2319 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2320 
2321   Level: advanced
2322 
2323 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2324           `PetscSF`, `PetscViewer`
2325 @*/
2326 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2327 {
2328   PetscBool ishdf5;
2329 
2330   PetscFunctionBegin;
2331   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2332   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2333   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2334   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2335   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2336   if (ishdf5) {
2337 #if defined(PETSC_HAVE_HDF5)
2338     PetscViewerFormat format;
2339     PetscCall(PetscViewerGetFormat(viewer, &format));
2340     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2341       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2342     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2343 #else
2344     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2345 #endif
2346   }
2347   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2348   PetscFunctionReturn(PETSC_SUCCESS);
2349 }
2350 
2351 /*@
2352   DMPlexLabelsLoad - Loads labels into a `DMPLEX`
2353 
2354   Collective
2355 
2356   Input Parameters:
2357 + dm                   - The `DM` into which the labels are loaded
2358 . viewer               - The `PetscViewer` for the saved labels
2359 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading `dm` from viewer
2360 
2361   Level: advanced
2362 
2363   Note:
2364   The `PetscSF` argument must not be NULL if the `DM` is distributed, otherwise an error occurs.
2365 
2366 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2367           `PetscSF`, `PetscViewer`
2368 @*/
2369 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2370 {
2371   PetscBool ishdf5;
2372 
2373   PetscFunctionBegin;
2374   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2375   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2376   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2377   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2378   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2379   if (ishdf5) {
2380 #if defined(PETSC_HAVE_HDF5)
2381     PetscViewerFormat format;
2382 
2383     PetscCall(PetscViewerGetFormat(viewer, &format));
2384     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2385       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2386     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2387 #else
2388     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2389 #endif
2390   }
2391   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2392   PetscFunctionReturn(PETSC_SUCCESS);
2393 }
2394 
2395 /*@
2396   DMPlexSectionLoad - Loads section into a `DMPLEX`
2397 
2398   Collective
2399 
2400   Input Parameters:
2401 + dm                   - The `DM` that represents the topology
2402 . viewer               - The `PetscViewer` that represents the on-disk section (sectionA)
2403 . sectiondm            - The `DM` into which the on-disk section (sectionA) is migrated
2404 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad(`) when loading dm from viewer
2405 
2406   Output Parameters:
2407 + 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)
2408 - 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)
2409 
2410   Level: advanced
2411 
2412   Notes:
2413   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.
2414 
2415   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.
2416 
2417   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.
2418 
2419   Example using 2 processes:
2420 .vb
2421   NX (number of points on dm): 4
2422   sectionA                   : the on-disk section
2423   vecA                       : a vector associated with sectionA
2424   sectionB                   : sectiondm's local section constructed in this function
2425   vecB (local)               : a vector associated with sectiondm's local section
2426   vecB (global)              : a vector associated with sectiondm's global section
2427 
2428                                      rank 0    rank 1
2429   vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2430   sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2431   sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2432   sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2433   [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2434   sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2435   sectionB->atlasDof             :     1 0 1 | 1 3
2436   sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2437   vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2438   vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2439 .ve
2440   where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2441 
2442 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`, `PetscSF`, `PetscViewer`
2443 @*/
2444 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
2445 {
2446   PetscBool ishdf5;
2447 
2448   PetscFunctionBegin;
2449   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2450   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2451   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2452   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2453   if (globalDofSF) PetscAssertPointer(globalDofSF, 5);
2454   if (localDofSF) PetscAssertPointer(localDofSF, 6);
2455   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2456   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2457   if (ishdf5) {
2458 #if defined(PETSC_HAVE_HDF5)
2459     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2460 #else
2461     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2462 #endif
2463   }
2464   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2465   PetscFunctionReturn(PETSC_SUCCESS);
2466 }
2467 
2468 /*@
2469   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2470 
2471   Collective
2472 
2473   Input Parameters:
2474 + dm        - The `DM` that represents the topology
2475 . viewer    - The `PetscViewer` that represents the on-disk vector data
2476 . sectiondm - The `DM` that contains the global section on which vec is defined
2477 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2478 - vec       - The global vector to set values of
2479 
2480   Level: advanced
2481 
2482   Notes:
2483   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.
2484 
2485   Calling sequence:
2486 .vb
2487        DMCreate(PETSC_COMM_WORLD, &dm);
2488        DMSetType(dm, DMPLEX);
2489        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2490        DMPlexTopologyLoad(dm, viewer, &sfX);
2491        DMClone(dm, &sectiondm);
2492        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2493        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2494        DMGetGlobalVector(sectiondm, &vec);
2495        PetscObjectSetName((PetscObject)vec, "vec_name");
2496        DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2497        DMRestoreGlobalVector(sectiondm, &vec);
2498        PetscSFDestroy(&gsf);
2499        PetscSFDestroy(&sfX);
2500        DMDestroy(&sectiondm);
2501        DMDestroy(&dm);
2502 .ve
2503 
2504 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2505           `PetscSF`, `PetscViewer`
2506 @*/
2507 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2508 {
2509   PetscBool ishdf5;
2510 
2511   PetscFunctionBegin;
2512   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2513   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2514   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2515   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2516   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2517   /* Check consistency */
2518   {
2519     PetscSection section;
2520     PetscBool    includesConstraints;
2521     PetscInt     m, m1;
2522 
2523     PetscCall(VecGetLocalSize(vec, &m1));
2524     PetscCall(DMGetGlobalSection(sectiondm, &section));
2525     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2526     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2527     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2528     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2529   }
2530   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2531   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2532   if (ishdf5) {
2533 #if defined(PETSC_HAVE_HDF5)
2534     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2535 #else
2536     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2537 #endif
2538   }
2539   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2540   PetscFunctionReturn(PETSC_SUCCESS);
2541 }
2542 
2543 /*@
2544   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2545 
2546   Collective
2547 
2548   Input Parameters:
2549 + dm        - The `DM` that represents the topology
2550 . viewer    - The `PetscViewer` that represents the on-disk vector data
2551 . sectiondm - The `DM` that contains the local section on which vec is defined
2552 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2553 - vec       - The local vector to set values of
2554 
2555   Level: advanced
2556 
2557   Notes:
2558   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.
2559 
2560   Calling sequence:
2561 .vb
2562        DMCreate(PETSC_COMM_WORLD, &dm);
2563        DMSetType(dm, DMPLEX);
2564        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2565        DMPlexTopologyLoad(dm, viewer, &sfX);
2566        DMClone(dm, &sectiondm);
2567        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2568        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2569        DMGetLocalVector(sectiondm, &vec);
2570        PetscObjectSetName((PetscObject)vec, "vec_name");
2571        DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2572        DMRestoreLocalVector(sectiondm, &vec);
2573        PetscSFDestroy(&lsf);
2574        PetscSFDestroy(&sfX);
2575        DMDestroy(&sectiondm);
2576        DMDestroy(&dm);
2577 .ve
2578 
2579 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2580           `PetscSF`, `PetscViewer`
2581 @*/
2582 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2583 {
2584   PetscBool ishdf5;
2585 
2586   PetscFunctionBegin;
2587   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2588   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2589   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2590   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2591   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2592   /* Check consistency */
2593   {
2594     PetscSection section;
2595     PetscBool    includesConstraints;
2596     PetscInt     m, m1;
2597 
2598     PetscCall(VecGetLocalSize(vec, &m1));
2599     PetscCall(DMGetLocalSection(sectiondm, &section));
2600     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2601     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2602     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2603     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2604   }
2605   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2606   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2607   if (ishdf5) {
2608 #if defined(PETSC_HAVE_HDF5)
2609     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2610 #else
2611     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2612 #endif
2613   }
2614   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2615   PetscFunctionReturn(PETSC_SUCCESS);
2616 }
2617 
2618 PetscErrorCode DMDestroy_Plex(DM dm)
2619 {
2620   DM_Plex *mesh = (DM_Plex *)dm->data;
2621 
2622   PetscFunctionBegin;
2623   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2624   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2625   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2626   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2627   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerviativeBoundaryValues_C", NULL));
2628   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2629   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2630   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2631   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2632   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2633   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2634   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2635   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2636   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetUseCeed_C", NULL));
2637   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetUseCeed_C", NULL));
2638   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", NULL));
2639   if (--mesh->refct > 0) PetscFunctionReturn(PETSC_SUCCESS);
2640   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2641   PetscCall(PetscFree(mesh->cones));
2642   PetscCall(PetscFree(mesh->coneOrientations));
2643   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2644   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2645   PetscCall(PetscFree(mesh->supports));
2646   PetscCall(PetscFree(mesh->cellTypes));
2647   PetscCall(DMPlexTransformDestroy(&mesh->tr));
2648   PetscCall(PetscFree(mesh->tetgenOpts));
2649   PetscCall(PetscFree(mesh->triangleOpts));
2650   PetscCall(PetscFree(mesh->transformType));
2651   PetscCall(PetscFree(mesh->distributionName));
2652   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2653   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2654   PetscCall(ISDestroy(&mesh->subpointIS));
2655   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2656   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2657   PetscCall(PetscSFDestroy(&mesh->periodic.face_sf));
2658   PetscCall(PetscSFDestroy(&mesh->periodic.composed_sf));
2659   PetscCall(ISDestroy(&mesh->periodic.periodic_points));
2660   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2661   PetscCall(ISDestroy(&mesh->anchorIS));
2662   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2663   PetscCall(PetscFree(mesh->parents));
2664   PetscCall(PetscFree(mesh->childIDs));
2665   PetscCall(PetscSectionDestroy(&mesh->childSection));
2666   PetscCall(PetscFree(mesh->children));
2667   PetscCall(DMDestroy(&mesh->referenceTree));
2668   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2669   PetscCall(PetscFree(mesh->neighbors));
2670   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2671   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2672   PetscCall(PetscFree(mesh));
2673   PetscFunctionReturn(PETSC_SUCCESS);
2674 }
2675 
2676 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2677 {
2678   PetscSection           sectionGlobal;
2679   PetscInt               bs = -1, mbs;
2680   PetscInt               localSize, localStart = 0;
2681   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2682   MatType                mtype;
2683   ISLocalToGlobalMapping ltog;
2684 
2685   PetscFunctionBegin;
2686   PetscCall(MatInitializePackage());
2687   mtype = dm->mattype;
2688   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2689   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2690   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2691   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2692   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2693   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2694   PetscCall(MatSetType(*J, mtype));
2695   PetscCall(MatSetFromOptions(*J));
2696   PetscCall(MatGetBlockSize(*J, &mbs));
2697   if (mbs > 1) bs = mbs;
2698   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2699   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2700   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2701   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2702   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2703   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2704   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2705   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2706   if (!isShell) {
2707     PetscBool fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2708     PetscInt *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2709     PetscInt  pStart, pEnd, p, dof, cdof, num_fields;
2710 
2711     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2712 
2713     PetscCall(PetscCalloc1(localSize, &pblocks));
2714     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2715     PetscCall(PetscSectionGetNumFields(sectionGlobal, &num_fields));
2716     for (p = pStart; p < pEnd; ++p) {
2717       switch (dm->blocking_type) {
2718       case DM_BLOCKING_TOPOLOGICAL_POINT: { // One block per topological point
2719         PetscInt bdof, offset;
2720 
2721         PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2722         PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2723         PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2724         for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = dof - cdof;
2725         dof  = dof < 0 ? -(dof + 1) : dof;
2726         bdof = cdof && (dof - cdof) ? 1 : dof;
2727         if (dof) {
2728           if (bs < 0) {
2729             bs = bdof;
2730           } else if (bs != bdof) {
2731             bs = 1;
2732           }
2733         }
2734       } break;
2735       case DM_BLOCKING_FIELD_NODE: {
2736         for (PetscInt field = 0; field < num_fields; field++) {
2737           PetscInt num_comp, bdof, offset;
2738           PetscCall(PetscSectionGetFieldComponents(sectionGlobal, field, &num_comp));
2739           PetscCall(PetscSectionGetFieldDof(sectionGlobal, p, field, &dof));
2740           if (dof < 0) continue;
2741           PetscCall(PetscSectionGetFieldOffset(sectionGlobal, p, field, &offset));
2742           PetscCall(PetscSectionGetFieldConstraintDof(sectionGlobal, p, field, &cdof));
2743           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);
2744           PetscInt num_nodes = dof / num_comp;
2745           for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = (dof - cdof) / num_nodes;
2746           // Handle possibly constant block size (unlikely)
2747           bdof = cdof && (dof - cdof) ? 1 : dof;
2748           if (dof) {
2749             if (bs < 0) {
2750               bs = bdof;
2751             } else if (bs != bdof) {
2752               bs = 1;
2753             }
2754           }
2755         }
2756       } break;
2757       }
2758     }
2759     /* Must have same blocksize on all procs (some might have no points) */
2760     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2761     bsLocal[1] = bs;
2762     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
2763     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2764     else bs = bsMinMax[0];
2765     bs = PetscMax(1, bs);
2766     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
2767     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2768       PetscCall(MatSetBlockSize(*J, bs));
2769       PetscCall(MatSetUp(*J));
2770     } else {
2771       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
2772       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2773       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2774     }
2775     { // Consolidate blocks
2776       PetscInt nblocks = 0;
2777       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
2778         if (pblocks[i] == 0) continue;
2779         pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2780         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]);
2781       }
2782       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2783     }
2784     PetscCall(PetscFree(pblocks));
2785   }
2786   PetscCall(MatSetDM(*J, dm));
2787   PetscFunctionReturn(PETSC_SUCCESS);
2788 }
2789 
2790 /*@
2791   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2792 
2793   Not Collective
2794 
2795   Input Parameter:
2796 . dm - The `DMPLEX`
2797 
2798   Output Parameter:
2799 . subsection - The subdomain section
2800 
2801   Level: developer
2802 
2803 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscSection`
2804 @*/
2805 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2806 {
2807   DM_Plex *mesh = (DM_Plex *)dm->data;
2808 
2809   PetscFunctionBegin;
2810   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2811   if (!mesh->subdomainSection) {
2812     PetscSection section;
2813     PetscSF      sf;
2814 
2815     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
2816     PetscCall(DMGetLocalSection(dm, &section));
2817     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
2818     PetscCall(PetscSFDestroy(&sf));
2819   }
2820   *subsection = mesh->subdomainSection;
2821   PetscFunctionReturn(PETSC_SUCCESS);
2822 }
2823 
2824 /*@
2825   DMPlexGetChart - Return the interval for all mesh points [`pStart`, `pEnd`)
2826 
2827   Not Collective
2828 
2829   Input Parameter:
2830 . dm - The `DMPLEX`
2831 
2832   Output Parameters:
2833 + pStart - The first mesh point
2834 - pEnd   - The upper bound for mesh points
2835 
2836   Level: beginner
2837 
2838 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`
2839 @*/
2840 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
2841 {
2842   DM_Plex *mesh = (DM_Plex *)dm->data;
2843 
2844   PetscFunctionBegin;
2845   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2846   if (mesh->tr) PetscCall(DMPlexTransformGetChart(mesh->tr, pStart, pEnd));
2847   else PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
2848   PetscFunctionReturn(PETSC_SUCCESS);
2849 }
2850 
2851 /*@
2852   DMPlexSetChart - Set the interval for all mesh points [`pStart`, `pEnd`)
2853 
2854   Not Collective
2855 
2856   Input Parameters:
2857 + dm     - The `DMPLEX`
2858 . pStart - The first mesh point
2859 - pEnd   - The upper bound for mesh points
2860 
2861   Level: beginner
2862 
2863 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetChart()`
2864 @*/
2865 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
2866 {
2867   DM_Plex *mesh = (DM_Plex *)dm->data;
2868 
2869   PetscFunctionBegin;
2870   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2871   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
2872   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
2873   PetscCall(PetscFree(mesh->cellTypes));
2874   PetscFunctionReturn(PETSC_SUCCESS);
2875 }
2876 
2877 /*@
2878   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
2879 
2880   Not Collective
2881 
2882   Input Parameters:
2883 + dm - The `DMPLEX`
2884 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
2885 
2886   Output Parameter:
2887 . size - The cone size for point `p`
2888 
2889   Level: beginner
2890 
2891 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
2892 @*/
2893 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
2894 {
2895   DM_Plex *mesh = (DM_Plex *)dm->data;
2896 
2897   PetscFunctionBegin;
2898   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2899   PetscAssertPointer(size, 3);
2900   if (mesh->tr) PetscCall(DMPlexTransformGetConeSize(mesh->tr, p, size));
2901   else PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
2902   PetscFunctionReturn(PETSC_SUCCESS);
2903 }
2904 
2905 /*@
2906   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
2907 
2908   Not Collective
2909 
2910   Input Parameters:
2911 + dm   - The `DMPLEX`
2912 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
2913 - size - The cone size for point `p`
2914 
2915   Level: beginner
2916 
2917   Note:
2918   This should be called after `DMPlexSetChart()`.
2919 
2920 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2921 @*/
2922 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
2923 {
2924   DM_Plex *mesh = (DM_Plex *)dm->data;
2925 
2926   PetscFunctionBegin;
2927   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2928   PetscCheck(!mesh->tr, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Cannot call DMPlexSetConeSize() on a mesh with a transform defined.");
2929   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
2930   PetscFunctionReturn(PETSC_SUCCESS);
2931 }
2932 
2933 /*@C
2934   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
2935 
2936   Not Collective
2937 
2938   Input Parameters:
2939 + dm - The `DMPLEX`
2940 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
2941 
2942   Output Parameter:
2943 . cone - An array of points which are on the in-edges for point `p`
2944 
2945   Level: beginner
2946 
2947   Fortran Notes:
2948   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
2949   `DMPlexRestoreCone()` is not needed/available in C.
2950 
2951 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`, `DMPlexRestoreCone()`
2952 @*/
2953 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
2954 {
2955   DM_Plex *mesh = (DM_Plex *)dm->data;
2956   PetscInt off;
2957 
2958   PetscFunctionBegin;
2959   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2960   PetscAssertPointer(cone, 3);
2961   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
2962   *cone = &mesh->cones[off];
2963   PetscFunctionReturn(PETSC_SUCCESS);
2964 }
2965 
2966 /*@C
2967   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
2968 
2969   Not Collective
2970 
2971   Input Parameters:
2972 + dm - The `DMPLEX`
2973 - p  - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
2974 
2975   Output Parameters:
2976 + pConesSection - `PetscSection` describing the layout of `pCones`
2977 - pCones        - An array of points which are on the in-edges for the point set `p`
2978 
2979   Level: intermediate
2980 
2981 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`, `PetscSection`, `IS`
2982 @*/
2983 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
2984 {
2985   PetscSection cs, newcs;
2986   PetscInt    *cones;
2987   PetscInt    *newarr = NULL;
2988   PetscInt     n;
2989 
2990   PetscFunctionBegin;
2991   PetscCall(DMPlexGetCones(dm, &cones));
2992   PetscCall(DMPlexGetConeSection(dm, &cs));
2993   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
2994   if (pConesSection) *pConesSection = newcs;
2995   if (pCones) {
2996     PetscCall(PetscSectionGetStorageSize(newcs, &n));
2997     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
2998   }
2999   PetscFunctionReturn(PETSC_SUCCESS);
3000 }
3001 
3002 /*@
3003   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
3004 
3005   Not Collective
3006 
3007   Input Parameters:
3008 + dm     - The `DMPLEX`
3009 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3010 
3011   Output Parameter:
3012 . expandedPoints - An array of vertices recursively expanded from input points
3013 
3014   Level: advanced
3015 
3016   Notes:
3017   Like `DMPlexGetConeRecursive()` but returns only the 0-depth `IS` (i.e. vertices only) and no sections.
3018 
3019   There is no corresponding Restore function, just call `ISDestroy()` on the returned `IS` to deallocate.
3020 
3021 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`,
3022           `DMPlexGetDepth()`, `IS`
3023 @*/
3024 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
3025 {
3026   IS      *expandedPointsAll;
3027   PetscInt depth;
3028 
3029   PetscFunctionBegin;
3030   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3031   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3032   PetscAssertPointer(expandedPoints, 3);
3033   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3034   *expandedPoints = expandedPointsAll[0];
3035   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
3036   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3037   PetscFunctionReturn(PETSC_SUCCESS);
3038 }
3039 
3040 /*@
3041   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).
3042 
3043   Not Collective
3044 
3045   Input Parameters:
3046 + dm     - The `DMPLEX`
3047 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3048 
3049   Output Parameters:
3050 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3051 . expandedPoints - (optional) An array of index sets with recursively expanded cones
3052 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3053 
3054   Level: advanced
3055 
3056   Notes:
3057   Like `DMPlexGetConeTuple()` but recursive.
3058 
3059   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.
3060   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
3061 
3062   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\:
3063   (1) DAG points in `expandedPoints`[d+1] with `depth` d+1 to their cone points in `expandedPoints`[d];
3064   (2) DAG points in `expandedPoints`[d+1] with `depth` in [0,d] to the same points in `expandedPoints`[d].
3065 
3066 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3067           `DMPlexGetDepth()`, `PetscSection`, `IS`
3068 @*/
3069 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3070 {
3071   const PetscInt *arr0 = NULL, *cone = NULL;
3072   PetscInt       *arr = NULL, *newarr = NULL;
3073   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
3074   IS             *expandedPoints_;
3075   PetscSection   *sections_;
3076 
3077   PetscFunctionBegin;
3078   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3079   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3080   if (depth) PetscAssertPointer(depth, 3);
3081   if (expandedPoints) PetscAssertPointer(expandedPoints, 4);
3082   if (sections) PetscAssertPointer(sections, 5);
3083   PetscCall(ISGetLocalSize(points, &n));
3084   PetscCall(ISGetIndices(points, &arr0));
3085   PetscCall(DMPlexGetDepth(dm, &depth_));
3086   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
3087   PetscCall(PetscCalloc1(depth_, &sections_));
3088   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
3089   for (d = depth_ - 1; d >= 0; d--) {
3090     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
3091     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
3092     for (i = 0; i < n; i++) {
3093       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
3094       if (arr[i] >= start && arr[i] < end) {
3095         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
3096         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
3097       } else {
3098         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
3099       }
3100     }
3101     PetscCall(PetscSectionSetUp(sections_[d]));
3102     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
3103     PetscCall(PetscMalloc1(newn, &newarr));
3104     for (i = 0; i < n; i++) {
3105       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
3106       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
3107       if (cn > 1) {
3108         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
3109         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
3110       } else {
3111         newarr[co] = arr[i];
3112       }
3113     }
3114     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
3115     arr = newarr;
3116     n   = newn;
3117   }
3118   PetscCall(ISRestoreIndices(points, &arr0));
3119   *depth = depth_;
3120   if (expandedPoints) *expandedPoints = expandedPoints_;
3121   else {
3122     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
3123     PetscCall(PetscFree(expandedPoints_));
3124   }
3125   if (sections) *sections = sections_;
3126   else {
3127     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
3128     PetscCall(PetscFree(sections_));
3129   }
3130   PetscFunctionReturn(PETSC_SUCCESS);
3131 }
3132 
3133 /*@
3134   DMPlexRestoreConeRecursive - Deallocates arrays created by `DMPlexGetConeRecursive()`
3135 
3136   Not Collective
3137 
3138   Input Parameters:
3139 + dm     - The `DMPLEX`
3140 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3141 
3142   Output Parameters:
3143 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3144 . expandedPoints - (optional) An array of recursively expanded cones
3145 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3146 
3147   Level: advanced
3148 
3149   Note:
3150   See `DMPlexGetConeRecursive()`
3151 
3152 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3153           `DMPlexGetDepth()`, `IS`, `PetscSection`
3154 @*/
3155 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3156 {
3157   PetscInt d, depth_;
3158 
3159   PetscFunctionBegin;
3160   PetscCall(DMPlexGetDepth(dm, &depth_));
3161   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
3162   if (depth) *depth = 0;
3163   if (expandedPoints) {
3164     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
3165     PetscCall(PetscFree(*expandedPoints));
3166   }
3167   if (sections) {
3168     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
3169     PetscCall(PetscFree(*sections));
3170   }
3171   PetscFunctionReturn(PETSC_SUCCESS);
3172 }
3173 
3174 /*@
3175   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
3176 
3177   Not Collective
3178 
3179   Input Parameters:
3180 + dm   - The `DMPLEX`
3181 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3182 - cone - An array of points which are on the in-edges for point `p`
3183 
3184   Level: beginner
3185 
3186   Note:
3187   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3188 
3189 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3190 @*/
3191 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3192 {
3193   DM_Plex *mesh = (DM_Plex *)dm->data;
3194   PetscInt dof, off, c;
3195 
3196   PetscFunctionBegin;
3197   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3198   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3199   if (dof) PetscAssertPointer(cone, 3);
3200   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3201   if (PetscDefined(USE_DEBUG)) {
3202     PetscInt pStart, pEnd;
3203     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3204     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);
3205     for (c = 0; c < dof; ++c) {
3206       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);
3207       mesh->cones[off + c] = cone[c];
3208     }
3209   } else {
3210     for (c = 0; c < dof; ++c) mesh->cones[off + c] = cone[c];
3211   }
3212   PetscFunctionReturn(PETSC_SUCCESS);
3213 }
3214 
3215 /*@C
3216   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3217 
3218   Not Collective
3219 
3220   Input Parameters:
3221 + dm - The `DMPLEX`
3222 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3223 
3224   Output Parameter:
3225 . coneOrientation - An array of orientations which are on the in-edges for point `p`. An orientation is an
3226                     integer giving the prescription for cone traversal.
3227 
3228   Level: beginner
3229 
3230   Note:
3231   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3232   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3233   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3234   with the identity.
3235 
3236   Fortran Notes:
3237   You must also call `DMPlexRestoreConeOrientation()` after you finish using the returned array.
3238   `DMPlexRestoreConeOrientation()` is not needed/available in C.
3239 
3240 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3241 @*/
3242 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3243 {
3244   DM_Plex *mesh = (DM_Plex *)dm->data;
3245   PetscInt off;
3246 
3247   PetscFunctionBegin;
3248   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3249   if (PetscDefined(USE_DEBUG)) {
3250     PetscInt dof;
3251     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3252     if (dof) PetscAssertPointer(coneOrientation, 3);
3253   }
3254   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3255 
3256   *coneOrientation = &mesh->coneOrientations[off];
3257   PetscFunctionReturn(PETSC_SUCCESS);
3258 }
3259 
3260 /*@
3261   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3262 
3263   Not Collective
3264 
3265   Input Parameters:
3266 + dm              - The `DMPLEX`
3267 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3268 - coneOrientation - An array of orientations
3269 
3270   Level: beginner
3271 
3272   Notes:
3273   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3274 
3275   The meaning of coneOrientation is detailed in `DMPlexGetConeOrientation()`.
3276 
3277 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3278 @*/
3279 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3280 {
3281   DM_Plex *mesh = (DM_Plex *)dm->data;
3282   PetscInt pStart, pEnd;
3283   PetscInt dof, off, c;
3284 
3285   PetscFunctionBegin;
3286   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3287   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3288   if (dof) PetscAssertPointer(coneOrientation, 3);
3289   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3290   if (PetscDefined(USE_DEBUG)) {
3291     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3292     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);
3293     for (c = 0; c < dof; ++c) {
3294       PetscInt cdof, o = coneOrientation[c];
3295 
3296       PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3297       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);
3298       mesh->coneOrientations[off + c] = o;
3299     }
3300   } else {
3301     for (c = 0; c < dof; ++c) mesh->coneOrientations[off + c] = coneOrientation[c];
3302   }
3303   PetscFunctionReturn(PETSC_SUCCESS);
3304 }
3305 
3306 /*@
3307   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3308 
3309   Not Collective
3310 
3311   Input Parameters:
3312 + dm        - The `DMPLEX`
3313 . p         - The point, which must lie in the chart set with `DMPlexSetChart()`
3314 . conePos   - The local index in the cone where the point should be put
3315 - conePoint - The mesh point to insert
3316 
3317   Level: beginner
3318 
3319 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3320 @*/
3321 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3322 {
3323   DM_Plex *mesh = (DM_Plex *)dm->data;
3324   PetscInt pStart, pEnd;
3325   PetscInt dof, off;
3326 
3327   PetscFunctionBegin;
3328   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3329   if (PetscDefined(USE_DEBUG)) {
3330     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3331     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);
3332     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);
3333     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3334     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);
3335   }
3336   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3337   mesh->cones[off + conePos] = conePoint;
3338   PetscFunctionReturn(PETSC_SUCCESS);
3339 }
3340 
3341 /*@
3342   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3343 
3344   Not Collective
3345 
3346   Input Parameters:
3347 + dm              - The `DMPLEX`
3348 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3349 . conePos         - The local index in the cone where the point should be put
3350 - coneOrientation - The point orientation to insert
3351 
3352   Level: beginner
3353 
3354   Note:
3355   The meaning of coneOrientation values is detailed in `DMPlexGetConeOrientation()`.
3356 
3357 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3358 @*/
3359 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3360 {
3361   DM_Plex *mesh = (DM_Plex *)dm->data;
3362   PetscInt pStart, pEnd;
3363   PetscInt dof, off;
3364 
3365   PetscFunctionBegin;
3366   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3367   if (PetscDefined(USE_DEBUG)) {
3368     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3369     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);
3370     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3371     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);
3372   }
3373   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3374   mesh->coneOrientations[off + conePos] = coneOrientation;
3375   PetscFunctionReturn(PETSC_SUCCESS);
3376 }
3377 
3378 /*@C
3379   DMPlexGetOrientedCone - Return the points and orientations on the in-edges for this point in the DAG
3380 
3381   Not collective
3382 
3383   Input Parameters:
3384 + dm - The DMPlex
3385 - p  - The point, which must lie in the chart set with DMPlexSetChart()
3386 
3387   Output Parameters:
3388 + cone - An array of points which are on the in-edges for point `p`
3389 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3390         integer giving the prescription for cone traversal.
3391 
3392   Level: beginner
3393 
3394   Notes:
3395   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3396   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3397   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3398   with the identity.
3399 
3400   Fortran Notes:
3401   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3402   `DMPlexRestoreCone()` is not needed/available in C.
3403 
3404 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3405 @*/
3406 PetscErrorCode DMPlexGetOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3407 {
3408   DM_Plex *mesh = (DM_Plex *)dm->data;
3409 
3410   PetscFunctionBegin;
3411   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3412   if (mesh->tr) {
3413     PetscCall(DMPlexTransformGetCone(mesh->tr, p, cone, ornt));
3414   } else {
3415     PetscInt off;
3416     if (PetscDefined(USE_DEBUG)) {
3417       PetscInt dof;
3418       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3419       if (dof) {
3420         if (cone) PetscAssertPointer(cone, 3);
3421         if (ornt) PetscAssertPointer(ornt, 4);
3422       }
3423     }
3424     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3425     if (cone) *cone = mesh->cones ? mesh->cones + off : NULL; // NULL + 0 is UB
3426     if (ornt) *ornt = mesh->coneOrientations ? mesh->coneOrientations + off : NULL;
3427   }
3428   PetscFunctionReturn(PETSC_SUCCESS);
3429 }
3430 
3431 /*@C
3432   DMPlexRestoreOrientedCone - Restore the points and orientations on the in-edges for this point in the DAG
3433 
3434   Not Collective
3435 
3436   Input Parameters:
3437 + dm   - The DMPlex
3438 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3439 . cone - An array of points which are on the in-edges for point p
3440 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3441         integer giving the prescription for cone traversal.
3442 
3443   Level: beginner
3444 
3445   Notes:
3446   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3447   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3448   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3449   with the identity.
3450 
3451   Fortran Notes:
3452   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3453   `DMPlexRestoreCone()` is not needed/available in C.
3454 
3455 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3456 @*/
3457 PetscErrorCode DMPlexRestoreOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3458 {
3459   DM_Plex *mesh = (DM_Plex *)dm->data;
3460 
3461   PetscFunctionBegin;
3462   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3463   if (mesh->tr) PetscCall(DMPlexTransformRestoreCone(mesh->tr, p, cone, ornt));
3464   PetscFunctionReturn(PETSC_SUCCESS);
3465 }
3466 
3467 /*@
3468   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3469 
3470   Not Collective
3471 
3472   Input Parameters:
3473 + dm - The `DMPLEX`
3474 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3475 
3476   Output Parameter:
3477 . size - The support size for point `p`
3478 
3479   Level: beginner
3480 
3481 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3482 @*/
3483 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3484 {
3485   DM_Plex *mesh = (DM_Plex *)dm->data;
3486 
3487   PetscFunctionBegin;
3488   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3489   PetscAssertPointer(size, 3);
3490   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3491   PetscFunctionReturn(PETSC_SUCCESS);
3492 }
3493 
3494 /*@
3495   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3496 
3497   Not Collective
3498 
3499   Input Parameters:
3500 + dm   - The `DMPLEX`
3501 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3502 - size - The support size for point `p`
3503 
3504   Level: beginner
3505 
3506   Note:
3507   This should be called after `DMPlexSetChart()`.
3508 
3509 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3510 @*/
3511 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3512 {
3513   DM_Plex *mesh = (DM_Plex *)dm->data;
3514 
3515   PetscFunctionBegin;
3516   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3517   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3518   PetscFunctionReturn(PETSC_SUCCESS);
3519 }
3520 
3521 /*@C
3522   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3523 
3524   Not Collective
3525 
3526   Input Parameters:
3527 + dm - The `DMPLEX`
3528 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3529 
3530   Output Parameter:
3531 . support - An array of points which are on the out-edges for point `p`
3532 
3533   Level: beginner
3534 
3535   Fortran Notes:
3536   You must also call `DMPlexRestoreSupport()` after you finish using the returned array.
3537   `DMPlexRestoreSupport()` is not needed/available in C.
3538 
3539 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3540 @*/
3541 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3542 {
3543   DM_Plex *mesh = (DM_Plex *)dm->data;
3544   PetscInt off;
3545 
3546   PetscFunctionBegin;
3547   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3548   PetscAssertPointer(support, 3);
3549   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3550   *support = mesh->supports ? mesh->supports + off : NULL; //NULL + 0 is UB
3551   PetscFunctionReturn(PETSC_SUCCESS);
3552 }
3553 
3554 /*@
3555   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3556 
3557   Not Collective
3558 
3559   Input Parameters:
3560 + dm      - The `DMPLEX`
3561 . p       - The point, which must lie in the chart set with `DMPlexSetChart()`
3562 - support - An array of points which are on the out-edges for point `p`
3563 
3564   Level: beginner
3565 
3566   Note:
3567   This should be called after all calls to `DMPlexSetSupportSize()` and `DMSetUp()`.
3568 
3569 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3570 @*/
3571 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3572 {
3573   DM_Plex *mesh = (DM_Plex *)dm->data;
3574   PetscInt pStart, pEnd;
3575   PetscInt dof, off, c;
3576 
3577   PetscFunctionBegin;
3578   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3579   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3580   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3581   if (dof) PetscAssertPointer(support, 3);
3582   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3583   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);
3584   for (c = 0; c < dof; ++c) {
3585     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);
3586     mesh->supports[off + c] = support[c];
3587   }
3588   PetscFunctionReturn(PETSC_SUCCESS);
3589 }
3590 
3591 /*@
3592   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3593 
3594   Not Collective
3595 
3596   Input Parameters:
3597 + dm           - The `DMPLEX`
3598 . p            - The point, which must lie in the chart set with `DMPlexSetChart()`
3599 . supportPos   - The local index in the cone where the point should be put
3600 - supportPoint - The mesh point to insert
3601 
3602   Level: beginner
3603 
3604 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3605 @*/
3606 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3607 {
3608   DM_Plex *mesh = (DM_Plex *)dm->data;
3609   PetscInt pStart, pEnd;
3610   PetscInt dof, off;
3611 
3612   PetscFunctionBegin;
3613   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3614   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3615   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3616   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3617   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);
3618   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);
3619   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);
3620   mesh->supports[off + supportPos] = supportPoint;
3621   PetscFunctionReturn(PETSC_SUCCESS);
3622 }
3623 
3624 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3625 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3626 {
3627   switch (ct) {
3628   case DM_POLYTOPE_SEGMENT:
3629     if (o == -1) return -2;
3630     break;
3631   case DM_POLYTOPE_TRIANGLE:
3632     if (o == -3) return -1;
3633     if (o == -2) return -3;
3634     if (o == -1) return -2;
3635     break;
3636   case DM_POLYTOPE_QUADRILATERAL:
3637     if (o == -4) return -2;
3638     if (o == -3) return -1;
3639     if (o == -2) return -4;
3640     if (o == -1) return -3;
3641     break;
3642   default:
3643     return o;
3644   }
3645   return o;
3646 }
3647 
3648 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3649 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3650 {
3651   switch (ct) {
3652   case DM_POLYTOPE_SEGMENT:
3653     if ((o == -2) || (o == 1)) return -1;
3654     if (o == -1) return 0;
3655     break;
3656   case DM_POLYTOPE_TRIANGLE:
3657     if (o == -3) return -2;
3658     if (o == -2) return -1;
3659     if (o == -1) return -3;
3660     break;
3661   case DM_POLYTOPE_QUADRILATERAL:
3662     if (o == -4) return -2;
3663     if (o == -3) return -1;
3664     if (o == -2) return -4;
3665     if (o == -1) return -3;
3666     break;
3667   default:
3668     return o;
3669   }
3670   return o;
3671 }
3672 
3673 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3674 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3675 {
3676   PetscInt pStart, pEnd, p;
3677 
3678   PetscFunctionBegin;
3679   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3680   for (p = pStart; p < pEnd; ++p) {
3681     const PetscInt *cone, *ornt;
3682     PetscInt        coneSize, c;
3683 
3684     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3685     PetscCall(DMPlexGetCone(dm, p, &cone));
3686     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3687     for (c = 0; c < coneSize; ++c) {
3688       DMPolytopeType ct;
3689       const PetscInt o = ornt[c];
3690 
3691       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3692       switch (ct) {
3693       case DM_POLYTOPE_SEGMENT:
3694         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3695         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3696         break;
3697       case DM_POLYTOPE_TRIANGLE:
3698         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3699         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3700         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3701         break;
3702       case DM_POLYTOPE_QUADRILATERAL:
3703         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3704         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3705         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3706         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3707         break;
3708       default:
3709         break;
3710       }
3711     }
3712   }
3713   PetscFunctionReturn(PETSC_SUCCESS);
3714 }
3715 
3716 static inline PetscErrorCode DMPlexGetTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3717 {
3718   DM_Plex *mesh = (DM_Plex *)dm->data;
3719 
3720   PetscFunctionBeginHot;
3721   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3722     if (useCone) {
3723       PetscCall(DMPlexGetConeSize(dm, p, size));
3724       PetscCall(DMPlexGetOrientedCone(dm, p, arr, ornt));
3725     } else {
3726       PetscCall(DMPlexGetSupportSize(dm, p, size));
3727       PetscCall(DMPlexGetSupport(dm, p, arr));
3728     }
3729   } else {
3730     if (useCone) {
3731       const PetscSection s   = mesh->coneSection;
3732       const PetscInt     ps  = p - s->pStart;
3733       const PetscInt     off = s->atlasOff[ps];
3734 
3735       *size = s->atlasDof[ps];
3736       *arr  = mesh->cones + off;
3737       *ornt = mesh->coneOrientations + off;
3738     } else {
3739       const PetscSection s   = mesh->supportSection;
3740       const PetscInt     ps  = p - s->pStart;
3741       const PetscInt     off = s->atlasOff[ps];
3742 
3743       *size = s->atlasDof[ps];
3744       *arr  = mesh->supports + off;
3745     }
3746   }
3747   PetscFunctionReturn(PETSC_SUCCESS);
3748 }
3749 
3750 static inline PetscErrorCode DMPlexRestoreTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3751 {
3752   DM_Plex *mesh = (DM_Plex *)dm->data;
3753 
3754   PetscFunctionBeginHot;
3755   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3756     if (useCone) PetscCall(DMPlexRestoreOrientedCone(dm, p, arr, ornt));
3757   }
3758   PetscFunctionReturn(PETSC_SUCCESS);
3759 }
3760 
3761 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3762 {
3763   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3764   PetscInt       *closure;
3765   const PetscInt *tmp = NULL, *tmpO = NULL;
3766   PetscInt        off = 0, tmpSize, t;
3767 
3768   PetscFunctionBeginHot;
3769   if (ornt) {
3770     PetscCall(DMPlexGetCellType(dm, p, &ct));
3771     if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3772   }
3773   if (*points) {
3774     closure = *points;
3775   } else {
3776     PetscInt maxConeSize, maxSupportSize;
3777     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3778     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
3779   }
3780   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3781   if (ct == DM_POLYTOPE_UNKNOWN) {
3782     closure[off++] = p;
3783     closure[off++] = 0;
3784     for (t = 0; t < tmpSize; ++t) {
3785       closure[off++] = tmp[t];
3786       closure[off++] = tmpO ? tmpO[t] : 0;
3787     }
3788   } else {
3789     const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, ornt);
3790 
3791     /* We assume that cells with a valid type have faces with a valid type */
3792     closure[off++] = p;
3793     closure[off++] = ornt;
3794     for (t = 0; t < tmpSize; ++t) {
3795       DMPolytopeType ft;
3796 
3797       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3798       closure[off++] = tmp[arr[t]];
3799       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3800     }
3801   }
3802   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3803   if (numPoints) *numPoints = tmpSize + 1;
3804   if (points) *points = closure;
3805   PetscFunctionReturn(PETSC_SUCCESS);
3806 }
3807 
3808 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
3809 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3810 {
3811   const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, o);
3812   const PetscInt *cone, *ornt;
3813   PetscInt       *pts, *closure = NULL;
3814   DMPolytopeType  ft;
3815   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3816   PetscInt        dim, coneSize, c, d, clSize, cl;
3817 
3818   PetscFunctionBeginHot;
3819   PetscCall(DMGetDimension(dm, &dim));
3820   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3821   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3822   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
3823   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
3824   maxSize       = PetscMax(coneSeries, supportSeries);
3825   if (*points) {
3826     pts = *points;
3827   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
3828   c        = 0;
3829   pts[c++] = point;
3830   pts[c++] = o;
3831   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
3832   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
3833   for (cl = 0; cl < clSize * 2; cl += 2) {
3834     pts[c++] = closure[cl];
3835     pts[c++] = closure[cl + 1];
3836   }
3837   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
3838   for (cl = 0; cl < clSize * 2; cl += 2) {
3839     pts[c++] = closure[cl];
3840     pts[c++] = closure[cl + 1];
3841   }
3842   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3843   for (d = 2; d < coneSize; ++d) {
3844     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
3845     pts[c++] = cone[arr[d * 2 + 0]];
3846     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
3847   }
3848   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3849   if (dim >= 3) {
3850     for (d = 2; d < coneSize; ++d) {
3851       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
3852       const PetscInt *fcone, *fornt;
3853       PetscInt        fconeSize, fc, i;
3854 
3855       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
3856       const PetscInt *farr = DMPolytopeTypeGetArrangment(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
3857       PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
3858       for (fc = 0; fc < fconeSize; ++fc) {
3859         const PetscInt cp = fcone[farr[fc * 2 + 0]];
3860         const PetscInt co = farr[fc * 2 + 1];
3861 
3862         for (i = 0; i < c; i += 2)
3863           if (pts[i] == cp) break;
3864         if (i == c) {
3865           PetscCall(DMPlexGetCellType(dm, cp, &ft));
3866           pts[c++] = cp;
3867           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
3868         }
3869       }
3870       PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
3871     }
3872   }
3873   *numPoints = c / 2;
3874   *points    = pts;
3875   PetscFunctionReturn(PETSC_SUCCESS);
3876 }
3877 
3878 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3879 {
3880   DMPolytopeType ct;
3881   PetscInt      *closure, *fifo;
3882   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
3883   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
3884   PetscInt       depth, maxSize;
3885 
3886   PetscFunctionBeginHot;
3887   PetscCall(DMPlexGetDepth(dm, &depth));
3888   if (depth == 1) {
3889     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
3890     PetscFunctionReturn(PETSC_SUCCESS);
3891   }
3892   PetscCall(DMPlexGetCellType(dm, p, &ct));
3893   if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3894   if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
3895     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
3896     PetscFunctionReturn(PETSC_SUCCESS);
3897   }
3898   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3899   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
3900   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
3901   maxSize       = PetscMax(coneSeries, supportSeries);
3902   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3903   if (*points) {
3904     closure = *points;
3905   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
3906   closure[closureSize++] = p;
3907   closure[closureSize++] = ornt;
3908   fifo[fifoSize++]       = p;
3909   fifo[fifoSize++]       = ornt;
3910   fifo[fifoSize++]       = ct;
3911   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
3912   while (fifoSize - fifoStart) {
3913     const PetscInt       q    = fifo[fifoStart++];
3914     const PetscInt       o    = fifo[fifoStart++];
3915     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
3916     const PetscInt      *qarr = DMPolytopeTypeGetArrangment(qt, o);
3917     const PetscInt      *tmp, *tmpO = NULL;
3918     PetscInt             tmpSize, t;
3919 
3920     if (PetscDefined(USE_DEBUG)) {
3921       PetscInt nO = DMPolytopeTypeGetNumArrangments(qt) / 2;
3922       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);
3923     }
3924     PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
3925     for (t = 0; t < tmpSize; ++t) {
3926       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
3927       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
3928       const PetscInt cp = tmp[ip];
3929       PetscCall(DMPlexGetCellType(dm, cp, &ct));
3930       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
3931       PetscInt       c;
3932 
3933       /* Check for duplicate */
3934       for (c = 0; c < closureSize; c += 2) {
3935         if (closure[c] == cp) break;
3936       }
3937       if (c == closureSize) {
3938         closure[closureSize++] = cp;
3939         closure[closureSize++] = co;
3940         fifo[fifoSize++]       = cp;
3941         fifo[fifoSize++]       = co;
3942         fifo[fifoSize++]       = ct;
3943       }
3944     }
3945     PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
3946   }
3947   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3948   if (numPoints) *numPoints = closureSize / 2;
3949   if (points) *points = closure;
3950   PetscFunctionReturn(PETSC_SUCCESS);
3951 }
3952 
3953 /*@C
3954   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
3955 
3956   Not Collective
3957 
3958   Input Parameters:
3959 + dm      - The `DMPLEX`
3960 . p       - The mesh point
3961 - useCone - `PETSC_TRUE` for the closure, otherwise return the star
3962 
3963   Input/Output Parameter:
3964 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
3965            if `NULL` on input, internal storage will be returned, otherwise the provided array is used
3966 
3967   Output Parameter:
3968 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
3969 
3970   Level: beginner
3971 
3972   Note:
3973   If using internal storage (points is `NULL` on input), each call overwrites the last output.
3974 
3975   Fortran Notes:
3976   The `numPoints` argument is not present in the Fortran binding since it is internal to the array.
3977 
3978 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3979 @*/
3980 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3981 {
3982   PetscFunctionBeginHot;
3983   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3984   if (numPoints) PetscAssertPointer(numPoints, 4);
3985   if (points) PetscAssertPointer(points, 5);
3986   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
3987   PetscFunctionReturn(PETSC_SUCCESS);
3988 }
3989 
3990 /*@C
3991   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
3992 
3993   Not Collective
3994 
3995   Input Parameters:
3996 + dm        - The `DMPLEX`
3997 . p         - The mesh point
3998 . useCone   - `PETSC_TRUE` for the closure, otherwise return the star
3999 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
4000 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
4001 
4002   Level: beginner
4003 
4004   Note:
4005   If not using internal storage (points is not `NULL` on input), this call is unnecessary
4006 
4007 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4008 @*/
4009 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4010 {
4011   PetscFunctionBeginHot;
4012   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4013   if (numPoints) *numPoints = 0;
4014   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
4015   PetscFunctionReturn(PETSC_SUCCESS);
4016 }
4017 
4018 /*@
4019   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
4020 
4021   Not Collective
4022 
4023   Input Parameter:
4024 . dm - The `DMPLEX`
4025 
4026   Output Parameters:
4027 + maxConeSize    - The maximum number of in-edges
4028 - maxSupportSize - The maximum number of out-edges
4029 
4030   Level: beginner
4031 
4032 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
4033 @*/
4034 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
4035 {
4036   DM_Plex *mesh = (DM_Plex *)dm->data;
4037 
4038   PetscFunctionBegin;
4039   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4040   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
4041   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
4042   PetscFunctionReturn(PETSC_SUCCESS);
4043 }
4044 
4045 PetscErrorCode DMSetUp_Plex(DM dm)
4046 {
4047   DM_Plex *mesh = (DM_Plex *)dm->data;
4048   PetscInt size, maxSupportSize;
4049 
4050   PetscFunctionBegin;
4051   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4052   PetscCall(PetscSectionSetUp(mesh->coneSection));
4053   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
4054   PetscCall(PetscMalloc1(size, &mesh->cones));
4055   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
4056   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4057   if (maxSupportSize) {
4058     PetscCall(PetscSectionSetUp(mesh->supportSection));
4059     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
4060     PetscCall(PetscMalloc1(size, &mesh->supports));
4061   }
4062   PetscFunctionReturn(PETSC_SUCCESS);
4063 }
4064 
4065 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
4066 {
4067   PetscFunctionBegin;
4068   if (subdm) PetscCall(DMClone(dm, subdm));
4069   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, is, subdm));
4070   if (subdm) (*subdm)->useNatural = dm->useNatural;
4071   if (dm->useNatural && dm->sfMigration) {
4072     PetscSF sfNatural;
4073 
4074     (*subdm)->sfMigration = dm->sfMigration;
4075     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
4076     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
4077     (*subdm)->sfNatural = sfNatural;
4078   }
4079   PetscFunctionReturn(PETSC_SUCCESS);
4080 }
4081 
4082 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
4083 {
4084   PetscInt i = 0;
4085 
4086   PetscFunctionBegin;
4087   PetscCall(DMClone(dms[0], superdm));
4088   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
4089   (*superdm)->useNatural = PETSC_FALSE;
4090   for (i = 0; i < len; i++) {
4091     if (dms[i]->useNatural && dms[i]->sfMigration) {
4092       PetscSF sfNatural;
4093 
4094       (*superdm)->sfMigration = dms[i]->sfMigration;
4095       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
4096       (*superdm)->useNatural = PETSC_TRUE;
4097       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
4098       (*superdm)->sfNatural = sfNatural;
4099       break;
4100     }
4101   }
4102   PetscFunctionReturn(PETSC_SUCCESS);
4103 }
4104 
4105 /*@
4106   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
4107 
4108   Not Collective
4109 
4110   Input Parameter:
4111 . dm - The `DMPLEX`
4112 
4113   Level: beginner
4114 
4115   Note:
4116   This should be called after all calls to `DMPlexSetCone()`
4117 
4118 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
4119 @*/
4120 PetscErrorCode DMPlexSymmetrize(DM dm)
4121 {
4122   DM_Plex  *mesh = (DM_Plex *)dm->data;
4123   PetscInt *offsets;
4124   PetscInt  supportSize;
4125   PetscInt  pStart, pEnd, p;
4126 
4127   PetscFunctionBegin;
4128   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4129   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
4130   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
4131   /* Calculate support sizes */
4132   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4133   for (p = pStart; p < pEnd; ++p) {
4134     PetscInt dof, off, c;
4135 
4136     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4137     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4138     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
4139   }
4140   PetscCall(PetscSectionSetUp(mesh->supportSection));
4141   /* Calculate supports */
4142   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
4143   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
4144   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
4145   for (p = pStart; p < pEnd; ++p) {
4146     PetscInt dof, off, c;
4147 
4148     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4149     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4150     for (c = off; c < off + dof; ++c) {
4151       const PetscInt q = mesh->cones[c];
4152       PetscInt       offS;
4153 
4154       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
4155 
4156       mesh->supports[offS + offsets[q]] = p;
4157       ++offsets[q];
4158     }
4159   }
4160   PetscCall(PetscFree(offsets));
4161   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
4162   PetscFunctionReturn(PETSC_SUCCESS);
4163 }
4164 
4165 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
4166 {
4167   IS stratumIS;
4168 
4169   PetscFunctionBegin;
4170   if (pStart >= pEnd) PetscFunctionReturn(PETSC_SUCCESS);
4171   if (PetscDefined(USE_DEBUG)) {
4172     PetscInt  qStart, qEnd, numLevels, level;
4173     PetscBool overlap = PETSC_FALSE;
4174     PetscCall(DMLabelGetNumValues(label, &numLevels));
4175     for (level = 0; level < numLevels; level++) {
4176       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4177       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
4178         overlap = PETSC_TRUE;
4179         break;
4180       }
4181     }
4182     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);
4183   }
4184   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
4185   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
4186   PetscCall(ISDestroy(&stratumIS));
4187   PetscFunctionReturn(PETSC_SUCCESS);
4188 }
4189 
4190 /*@
4191   DMPlexStratify - Computes the strata for all points in the `DMPLEX`
4192 
4193   Collective
4194 
4195   Input Parameter:
4196 . dm - The `DMPLEX`
4197 
4198   Level: beginner
4199 
4200   Notes:
4201   The strata group all points of the same grade, and this function calculates the strata. This
4202   grade can be seen as the height (or depth) of the point in the DAG.
4203 
4204   The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
4205   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram).
4206   Concretely, `DMPlexStratify()` creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
4207   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
4208   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through `DMPlexGetDepthLabel()` or `DMPlexGetDepthStratum()`, or
4209   manually via `DMGetLabel()`.  The height is defined implicitly by height = maxDimension - depth, and can be accessed
4210   via `DMPlexGetHeightStratum()`.  For example, cells have height 0 and faces have height 1.
4211 
4212   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
4213   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
4214   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
4215   to interpolate only that one (e0), so that
4216 .vb
4217   cone(c0) = {e0, v2}
4218   cone(e0) = {v0, v1}
4219 .ve
4220   If `DMPlexStratify()` is run on this mesh, it will give depths
4221 .vb
4222    depth 0 = {v0, v1, v2}
4223    depth 1 = {e0, c0}
4224 .ve
4225   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
4226 
4227   `DMPlexStratify()` should be called after all calls to `DMPlexSymmetrize()`
4228 
4229 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
4230 @*/
4231 PetscErrorCode DMPlexStratify(DM dm)
4232 {
4233   DM_Plex *mesh = (DM_Plex *)dm->data;
4234   DMLabel  label;
4235   PetscInt pStart, pEnd, p;
4236   PetscInt numRoots = 0, numLeaves = 0;
4237 
4238   PetscFunctionBegin;
4239   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4240   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
4241 
4242   /* Create depth label */
4243   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4244   PetscCall(DMCreateLabel(dm, "depth"));
4245   PetscCall(DMPlexGetDepthLabel(dm, &label));
4246 
4247   {
4248     /* Initialize roots and count leaves */
4249     PetscInt sMin = PETSC_MAX_INT;
4250     PetscInt sMax = PETSC_MIN_INT;
4251     PetscInt coneSize, supportSize;
4252 
4253     for (p = pStart; p < pEnd; ++p) {
4254       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4255       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4256       if (!coneSize && supportSize) {
4257         sMin = PetscMin(p, sMin);
4258         sMax = PetscMax(p, sMax);
4259         ++numRoots;
4260       } else if (!supportSize && coneSize) {
4261         ++numLeaves;
4262       } else if (!supportSize && !coneSize) {
4263         /* Isolated points */
4264         sMin = PetscMin(p, sMin);
4265         sMax = PetscMax(p, sMax);
4266       }
4267     }
4268     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4269   }
4270 
4271   if (numRoots + numLeaves == (pEnd - pStart)) {
4272     PetscInt sMin = PETSC_MAX_INT;
4273     PetscInt sMax = PETSC_MIN_INT;
4274     PetscInt coneSize, supportSize;
4275 
4276     for (p = pStart; p < pEnd; ++p) {
4277       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4278       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4279       if (!supportSize && coneSize) {
4280         sMin = PetscMin(p, sMin);
4281         sMax = PetscMax(p, sMax);
4282       }
4283     }
4284     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4285   } else {
4286     PetscInt level = 0;
4287     PetscInt qStart, qEnd, q;
4288 
4289     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4290     while (qEnd > qStart) {
4291       PetscInt sMin = PETSC_MAX_INT;
4292       PetscInt sMax = PETSC_MIN_INT;
4293 
4294       for (q = qStart; q < qEnd; ++q) {
4295         const PetscInt *support;
4296         PetscInt        supportSize, s;
4297 
4298         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4299         PetscCall(DMPlexGetSupport(dm, q, &support));
4300         for (s = 0; s < supportSize; ++s) {
4301           sMin = PetscMin(support[s], sMin);
4302           sMax = PetscMax(support[s], sMax);
4303         }
4304       }
4305       PetscCall(DMLabelGetNumValues(label, &level));
4306       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4307       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4308     }
4309   }
4310   { /* just in case there is an empty process */
4311     PetscInt numValues, maxValues = 0, v;
4312 
4313     PetscCall(DMLabelGetNumValues(label, &numValues));
4314     PetscCall(MPIU_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4315     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4316   }
4317   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4318   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4319   PetscFunctionReturn(PETSC_SUCCESS);
4320 }
4321 
4322 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4323 {
4324   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4325   PetscInt       dim, depth, pheight, coneSize;
4326 
4327   PetscFunctionBeginHot;
4328   PetscCall(DMGetDimension(dm, &dim));
4329   PetscCall(DMPlexGetDepth(dm, &depth));
4330   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4331   pheight = depth - pdepth;
4332   if (depth <= 1) {
4333     switch (pdepth) {
4334     case 0:
4335       ct = DM_POLYTOPE_POINT;
4336       break;
4337     case 1:
4338       switch (coneSize) {
4339       case 2:
4340         ct = DM_POLYTOPE_SEGMENT;
4341         break;
4342       case 3:
4343         ct = DM_POLYTOPE_TRIANGLE;
4344         break;
4345       case 4:
4346         switch (dim) {
4347         case 2:
4348           ct = DM_POLYTOPE_QUADRILATERAL;
4349           break;
4350         case 3:
4351           ct = DM_POLYTOPE_TETRAHEDRON;
4352           break;
4353         default:
4354           break;
4355         }
4356         break;
4357       case 5:
4358         ct = DM_POLYTOPE_PYRAMID;
4359         break;
4360       case 6:
4361         ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4362         break;
4363       case 8:
4364         ct = DM_POLYTOPE_HEXAHEDRON;
4365         break;
4366       default:
4367         break;
4368       }
4369     }
4370   } else {
4371     if (pdepth == 0) {
4372       ct = DM_POLYTOPE_POINT;
4373     } else if (pheight == 0) {
4374       switch (dim) {
4375       case 1:
4376         switch (coneSize) {
4377         case 2:
4378           ct = DM_POLYTOPE_SEGMENT;
4379           break;
4380         default:
4381           break;
4382         }
4383         break;
4384       case 2:
4385         switch (coneSize) {
4386         case 3:
4387           ct = DM_POLYTOPE_TRIANGLE;
4388           break;
4389         case 4:
4390           ct = DM_POLYTOPE_QUADRILATERAL;
4391           break;
4392         default:
4393           break;
4394         }
4395         break;
4396       case 3:
4397         switch (coneSize) {
4398         case 4:
4399           ct = DM_POLYTOPE_TETRAHEDRON;
4400           break;
4401         case 5: {
4402           const PetscInt *cone;
4403           PetscInt        faceConeSize;
4404 
4405           PetscCall(DMPlexGetCone(dm, p, &cone));
4406           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4407           switch (faceConeSize) {
4408           case 3:
4409             ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4410             break;
4411           case 4:
4412             ct = DM_POLYTOPE_PYRAMID;
4413             break;
4414           }
4415         } break;
4416         case 6:
4417           ct = DM_POLYTOPE_HEXAHEDRON;
4418           break;
4419         default:
4420           break;
4421         }
4422         break;
4423       default:
4424         break;
4425       }
4426     } else if (pheight > 0) {
4427       switch (coneSize) {
4428       case 2:
4429         ct = DM_POLYTOPE_SEGMENT;
4430         break;
4431       case 3:
4432         ct = DM_POLYTOPE_TRIANGLE;
4433         break;
4434       case 4:
4435         ct = DM_POLYTOPE_QUADRILATERAL;
4436         break;
4437       default:
4438         break;
4439       }
4440     }
4441   }
4442   *pt = ct;
4443   PetscFunctionReturn(PETSC_SUCCESS);
4444 }
4445 
4446 /*@
4447   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4448 
4449   Collective
4450 
4451   Input Parameter:
4452 . dm - The `DMPLEX`
4453 
4454   Level: developer
4455 
4456   Note:
4457   This function is normally called automatically when a cell type is requested. It creates an
4458   internal `DMLabel` named "celltype" which can be directly accessed using `DMGetLabel()`. A user may disable
4459   automatic creation by creating the label manually, using `DMCreateLabel`(dm, "celltype").
4460 
4461   `DMPlexComputeCellTypes()` should be called after all calls to `DMPlexSymmetrize()` and `DMPlexStratify()`
4462 
4463 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4464 @*/
4465 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4466 {
4467   DM_Plex *mesh;
4468   DMLabel  ctLabel;
4469   PetscInt pStart, pEnd, p;
4470 
4471   PetscFunctionBegin;
4472   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4473   mesh = (DM_Plex *)dm->data;
4474   PetscCall(DMCreateLabel(dm, "celltype"));
4475   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4476   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4477   PetscCall(PetscFree(mesh->cellTypes));
4478   PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
4479   for (p = pStart; p < pEnd; ++p) {
4480     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4481     PetscInt       pdepth;
4482 
4483     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4484     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4485     PetscCheck(ct != DM_POLYTOPE_UNKNOWN, PETSC_COMM_SELF, PETSC_ERR_SUP, "Point %" PetscInt_FMT " is screwed up", p);
4486     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4487     mesh->cellTypes[p - pStart].value_as_uint8 = ct;
4488   }
4489   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4490   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4491   PetscFunctionReturn(PETSC_SUCCESS);
4492 }
4493 
4494 /*@C
4495   DMPlexGetJoin - Get an array for the join of the set of points
4496 
4497   Not Collective
4498 
4499   Input Parameters:
4500 + dm        - The `DMPLEX` object
4501 . numPoints - The number of input points for the join
4502 - points    - The input points
4503 
4504   Output Parameters:
4505 + numCoveredPoints - The number of points in the join
4506 - coveredPoints    - The points in the join
4507 
4508   Level: intermediate
4509 
4510   Note:
4511   Currently, this is restricted to a single level join
4512 
4513   Fortran Notes:
4514   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4515 
4516 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4517 @*/
4518 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4519 {
4520   DM_Plex  *mesh = (DM_Plex *)dm->data;
4521   PetscInt *join[2];
4522   PetscInt  joinSize, i = 0;
4523   PetscInt  dof, off, p, c, m;
4524   PetscInt  maxSupportSize;
4525 
4526   PetscFunctionBegin;
4527   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4528   PetscAssertPointer(points, 3);
4529   PetscAssertPointer(numCoveredPoints, 4);
4530   PetscAssertPointer(coveredPoints, 5);
4531   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4532   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4533   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4534   /* Copy in support of first point */
4535   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4536   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4537   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4538   /* Check each successive support */
4539   for (p = 1; p < numPoints; ++p) {
4540     PetscInt newJoinSize = 0;
4541 
4542     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4543     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4544     for (c = 0; c < dof; ++c) {
4545       const PetscInt point = mesh->supports[off + c];
4546 
4547       for (m = 0; m < joinSize; ++m) {
4548         if (point == join[i][m]) {
4549           join[1 - i][newJoinSize++] = point;
4550           break;
4551         }
4552       }
4553     }
4554     joinSize = newJoinSize;
4555     i        = 1 - i;
4556   }
4557   *numCoveredPoints = joinSize;
4558   *coveredPoints    = join[i];
4559   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4560   PetscFunctionReturn(PETSC_SUCCESS);
4561 }
4562 
4563 /*@C
4564   DMPlexRestoreJoin - Restore an array for the join of the set of points
4565 
4566   Not Collective
4567 
4568   Input Parameters:
4569 + dm        - The `DMPLEX` object
4570 . numPoints - The number of input points for the join
4571 - points    - The input points
4572 
4573   Output Parameters:
4574 + numCoveredPoints - The number of points in the join
4575 - coveredPoints    - The points in the join
4576 
4577   Level: intermediate
4578 
4579   Fortran Notes:
4580   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4581 
4582 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4583 @*/
4584 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4585 {
4586   PetscFunctionBegin;
4587   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4588   if (points) PetscAssertPointer(points, 3);
4589   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4590   PetscAssertPointer(coveredPoints, 5);
4591   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4592   if (numCoveredPoints) *numCoveredPoints = 0;
4593   PetscFunctionReturn(PETSC_SUCCESS);
4594 }
4595 
4596 /*@C
4597   DMPlexGetFullJoin - Get an array for the join of the set of points
4598 
4599   Not Collective
4600 
4601   Input Parameters:
4602 + dm        - The `DMPLEX` object
4603 . numPoints - The number of input points for the join
4604 - points    - The input points
4605 
4606   Output Parameters:
4607 + numCoveredPoints - The number of points in the join
4608 - coveredPoints    - The points in the join
4609 
4610   Level: intermediate
4611 
4612   Fortran Notes:
4613   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4614 
4615 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4616 @*/
4617 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4618 {
4619   PetscInt *offsets, **closures;
4620   PetscInt *join[2];
4621   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4622   PetscInt  p, d, c, m, ms;
4623 
4624   PetscFunctionBegin;
4625   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4626   PetscAssertPointer(points, 3);
4627   PetscAssertPointer(numCoveredPoints, 4);
4628   PetscAssertPointer(coveredPoints, 5);
4629 
4630   PetscCall(DMPlexGetDepth(dm, &depth));
4631   PetscCall(PetscCalloc1(numPoints, &closures));
4632   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4633   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4634   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4635   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4636   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4637 
4638   for (p = 0; p < numPoints; ++p) {
4639     PetscInt closureSize;
4640 
4641     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4642 
4643     offsets[p * (depth + 2) + 0] = 0;
4644     for (d = 0; d < depth + 1; ++d) {
4645       PetscInt pStart, pEnd, i;
4646 
4647       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4648       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4649         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4650           offsets[p * (depth + 2) + d + 1] = i;
4651           break;
4652         }
4653       }
4654       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4655     }
4656     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);
4657   }
4658   for (d = 0; d < depth + 1; ++d) {
4659     PetscInt dof;
4660 
4661     /* Copy in support of first point */
4662     dof = offsets[d + 1] - offsets[d];
4663     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4664     /* Check each successive cone */
4665     for (p = 1; p < numPoints && joinSize; ++p) {
4666       PetscInt newJoinSize = 0;
4667 
4668       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
4669       for (c = 0; c < dof; ++c) {
4670         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
4671 
4672         for (m = 0; m < joinSize; ++m) {
4673           if (point == join[i][m]) {
4674             join[1 - i][newJoinSize++] = point;
4675             break;
4676           }
4677         }
4678       }
4679       joinSize = newJoinSize;
4680       i        = 1 - i;
4681     }
4682     if (joinSize) break;
4683   }
4684   *numCoveredPoints = joinSize;
4685   *coveredPoints    = join[i];
4686   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4687   PetscCall(PetscFree(closures));
4688   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4689   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
4690   PetscFunctionReturn(PETSC_SUCCESS);
4691 }
4692 
4693 /*@C
4694   DMPlexGetMeet - Get an array for the meet of the set of points
4695 
4696   Not Collective
4697 
4698   Input Parameters:
4699 + dm        - The `DMPLEX` object
4700 . numPoints - The number of input points for the meet
4701 - points    - The input points
4702 
4703   Output Parameters:
4704 + numCoveringPoints - The number of points in the meet
4705 - coveringPoints    - The points in the meet
4706 
4707   Level: intermediate
4708 
4709   Note:
4710   Currently, this is restricted to a single level meet
4711 
4712   Fortran Notes:
4713   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4714 
4715 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4716 @*/
4717 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt **coveringPoints)
4718 {
4719   DM_Plex  *mesh = (DM_Plex *)dm->data;
4720   PetscInt *meet[2];
4721   PetscInt  meetSize, i = 0;
4722   PetscInt  dof, off, p, c, m;
4723   PetscInt  maxConeSize;
4724 
4725   PetscFunctionBegin;
4726   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4727   PetscAssertPointer(points, 3);
4728   PetscAssertPointer(numCoveringPoints, 4);
4729   PetscAssertPointer(coveringPoints, 5);
4730   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4731   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4732   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4733   /* Copy in cone of first point */
4734   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4735   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4736   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
4737   /* Check each successive cone */
4738   for (p = 1; p < numPoints; ++p) {
4739     PetscInt newMeetSize = 0;
4740 
4741     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4742     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4743     for (c = 0; c < dof; ++c) {
4744       const PetscInt point = mesh->cones[off + c];
4745 
4746       for (m = 0; m < meetSize; ++m) {
4747         if (point == meet[i][m]) {
4748           meet[1 - i][newMeetSize++] = point;
4749           break;
4750         }
4751       }
4752     }
4753     meetSize = newMeetSize;
4754     i        = 1 - i;
4755   }
4756   *numCoveringPoints = meetSize;
4757   *coveringPoints    = meet[i];
4758   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
4759   PetscFunctionReturn(PETSC_SUCCESS);
4760 }
4761 
4762 /*@C
4763   DMPlexRestoreMeet - Restore an array for the meet of the set of points
4764 
4765   Not Collective
4766 
4767   Input Parameters:
4768 + dm        - The `DMPLEX` object
4769 . numPoints - The number of input points for the meet
4770 - points    - The input points
4771 
4772   Output Parameters:
4773 + numCoveredPoints - The number of points in the meet
4774 - coveredPoints    - The points in the meet
4775 
4776   Level: intermediate
4777 
4778   Fortran Notes:
4779   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4780 
4781 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
4782 @*/
4783 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4784 {
4785   PetscFunctionBegin;
4786   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4787   if (points) PetscAssertPointer(points, 3);
4788   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4789   PetscAssertPointer(coveredPoints, 5);
4790   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4791   if (numCoveredPoints) *numCoveredPoints = 0;
4792   PetscFunctionReturn(PETSC_SUCCESS);
4793 }
4794 
4795 /*@C
4796   DMPlexGetFullMeet - Get an array for the meet of the set of points
4797 
4798   Not Collective
4799 
4800   Input Parameters:
4801 + dm        - The `DMPLEX` object
4802 . numPoints - The number of input points for the meet
4803 - points    - The input points
4804 
4805   Output Parameters:
4806 + numCoveredPoints - The number of points in the meet
4807 - coveredPoints    - The points in the meet
4808 
4809   Level: intermediate
4810 
4811   Fortran Notes:
4812   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4813 
4814 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4815 @*/
4816 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4817 {
4818   PetscInt *offsets, **closures;
4819   PetscInt *meet[2];
4820   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
4821   PetscInt  p, h, c, m, mc;
4822 
4823   PetscFunctionBegin;
4824   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4825   PetscAssertPointer(points, 3);
4826   PetscAssertPointer(numCoveredPoints, 4);
4827   PetscAssertPointer(coveredPoints, 5);
4828 
4829   PetscCall(DMPlexGetDepth(dm, &height));
4830   PetscCall(PetscMalloc1(numPoints, &closures));
4831   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4832   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
4833   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
4834   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
4835   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
4836 
4837   for (p = 0; p < numPoints; ++p) {
4838     PetscInt closureSize;
4839 
4840     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
4841 
4842     offsets[p * (height + 2) + 0] = 0;
4843     for (h = 0; h < height + 1; ++h) {
4844       PetscInt pStart, pEnd, i;
4845 
4846       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
4847       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
4848         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4849           offsets[p * (height + 2) + h + 1] = i;
4850           break;
4851         }
4852       }
4853       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
4854     }
4855     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);
4856   }
4857   for (h = 0; h < height + 1; ++h) {
4858     PetscInt dof;
4859 
4860     /* Copy in cone of first point */
4861     dof = offsets[h + 1] - offsets[h];
4862     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
4863     /* Check each successive cone */
4864     for (p = 1; p < numPoints && meetSize; ++p) {
4865       PetscInt newMeetSize = 0;
4866 
4867       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
4868       for (c = 0; c < dof; ++c) {
4869         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
4870 
4871         for (m = 0; m < meetSize; ++m) {
4872           if (point == meet[i][m]) {
4873             meet[1 - i][newMeetSize++] = point;
4874             break;
4875           }
4876         }
4877       }
4878       meetSize = newMeetSize;
4879       i        = 1 - i;
4880     }
4881     if (meetSize) break;
4882   }
4883   *numCoveredPoints = meetSize;
4884   *coveredPoints    = meet[i];
4885   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
4886   PetscCall(PetscFree(closures));
4887   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4888   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
4889   PetscFunctionReturn(PETSC_SUCCESS);
4890 }
4891 
4892 /*@C
4893   DMPlexEqual - Determine if two `DM` have the same topology
4894 
4895   Not Collective
4896 
4897   Input Parameters:
4898 + dmA - A `DMPLEX` object
4899 - dmB - A `DMPLEX` object
4900 
4901   Output Parameter:
4902 . equal - `PETSC_TRUE` if the topologies are identical
4903 
4904   Level: intermediate
4905 
4906   Note:
4907   We are not solving graph isomorphism, so we do not permute.
4908 
4909 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
4910 @*/
4911 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
4912 {
4913   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
4914 
4915   PetscFunctionBegin;
4916   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
4917   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
4918   PetscAssertPointer(equal, 3);
4919 
4920   *equal = PETSC_FALSE;
4921   PetscCall(DMPlexGetDepth(dmA, &depth));
4922   PetscCall(DMPlexGetDepth(dmB, &depthB));
4923   if (depth != depthB) PetscFunctionReturn(PETSC_SUCCESS);
4924   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
4925   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
4926   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(PETSC_SUCCESS);
4927   for (p = pStart; p < pEnd; ++p) {
4928     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
4929     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
4930 
4931     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
4932     PetscCall(DMPlexGetCone(dmA, p, &cone));
4933     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
4934     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
4935     PetscCall(DMPlexGetCone(dmB, p, &coneB));
4936     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
4937     if (coneSize != coneSizeB) PetscFunctionReturn(PETSC_SUCCESS);
4938     for (c = 0; c < coneSize; ++c) {
4939       if (cone[c] != coneB[c]) PetscFunctionReturn(PETSC_SUCCESS);
4940       if (ornt[c] != orntB[c]) PetscFunctionReturn(PETSC_SUCCESS);
4941     }
4942     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
4943     PetscCall(DMPlexGetSupport(dmA, p, &support));
4944     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
4945     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
4946     if (supportSize != supportSizeB) PetscFunctionReturn(PETSC_SUCCESS);
4947     for (s = 0; s < supportSize; ++s) {
4948       if (support[s] != supportB[s]) PetscFunctionReturn(PETSC_SUCCESS);
4949     }
4950   }
4951   *equal = PETSC_TRUE;
4952   PetscFunctionReturn(PETSC_SUCCESS);
4953 }
4954 
4955 /*@C
4956   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
4957 
4958   Not Collective
4959 
4960   Input Parameters:
4961 + dm         - The `DMPLEX`
4962 . cellDim    - The cell dimension
4963 - numCorners - The number of vertices on a cell
4964 
4965   Output Parameter:
4966 . numFaceVertices - The number of vertices on a face
4967 
4968   Level: developer
4969 
4970   Note:
4971   Of course this can only work for a restricted set of symmetric shapes
4972 
4973 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
4974 @*/
4975 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
4976 {
4977   MPI_Comm comm;
4978 
4979   PetscFunctionBegin;
4980   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
4981   PetscAssertPointer(numFaceVertices, 4);
4982   switch (cellDim) {
4983   case 0:
4984     *numFaceVertices = 0;
4985     break;
4986   case 1:
4987     *numFaceVertices = 1;
4988     break;
4989   case 2:
4990     switch (numCorners) {
4991     case 3:                 /* triangle */
4992       *numFaceVertices = 2; /* Edge has 2 vertices */
4993       break;
4994     case 4:                 /* quadrilateral */
4995       *numFaceVertices = 2; /* Edge has 2 vertices */
4996       break;
4997     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
4998       *numFaceVertices = 3; /* Edge has 3 vertices */
4999       break;
5000     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
5001       *numFaceVertices = 3; /* Edge has 3 vertices */
5002       break;
5003     default:
5004       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5005     }
5006     break;
5007   case 3:
5008     switch (numCorners) {
5009     case 4:                 /* tetradehdron */
5010       *numFaceVertices = 3; /* Face has 3 vertices */
5011       break;
5012     case 6:                 /* tet cohesive cells */
5013       *numFaceVertices = 4; /* Face has 4 vertices */
5014       break;
5015     case 8:                 /* hexahedron */
5016       *numFaceVertices = 4; /* Face has 4 vertices */
5017       break;
5018     case 9:                 /* tet cohesive Lagrange cells */
5019       *numFaceVertices = 6; /* Face has 6 vertices */
5020       break;
5021     case 10:                /* quadratic tetrahedron */
5022       *numFaceVertices = 6; /* Face has 6 vertices */
5023       break;
5024     case 12:                /* hex cohesive Lagrange cells */
5025       *numFaceVertices = 6; /* Face has 6 vertices */
5026       break;
5027     case 18:                /* quadratic tet cohesive Lagrange cells */
5028       *numFaceVertices = 6; /* Face has 6 vertices */
5029       break;
5030     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
5031       *numFaceVertices = 9; /* Face has 9 vertices */
5032       break;
5033     default:
5034       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5035     }
5036     break;
5037   default:
5038     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
5039   }
5040   PetscFunctionReturn(PETSC_SUCCESS);
5041 }
5042 
5043 /*@
5044   DMPlexGetDepthLabel - Get the `DMLabel` recording the depth of each point
5045 
5046   Not Collective
5047 
5048   Input Parameter:
5049 . dm - The `DMPLEX` object
5050 
5051   Output Parameter:
5052 . depthLabel - The `DMLabel` recording point depth
5053 
5054   Level: developer
5055 
5056 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
5057 @*/
5058 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
5059 {
5060   PetscFunctionBegin;
5061   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5062   PetscAssertPointer(depthLabel, 2);
5063   *depthLabel = dm->depthLabel;
5064   PetscFunctionReturn(PETSC_SUCCESS);
5065 }
5066 
5067 /*@
5068   DMPlexGetDepth - Get the depth of the DAG representing this mesh
5069 
5070   Not Collective
5071 
5072   Input Parameter:
5073 . dm - The `DMPLEX` object
5074 
5075   Output Parameter:
5076 . depth - The number of strata (breadth first levels) in the DAG
5077 
5078   Level: developer
5079 
5080   Notes:
5081   This returns maximum of point depths over all points, i.e. maximum value of the label returned by `DMPlexGetDepthLabel()`.
5082 
5083   The point depth is described more in detail in `DMPlexGetDepthStratum()`.
5084 
5085   An empty mesh gives -1.
5086 
5087 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
5088 @*/
5089 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
5090 {
5091   DM_Plex *mesh = (DM_Plex *)dm->data;
5092   DMLabel  label;
5093   PetscInt d = 0;
5094 
5095   PetscFunctionBegin;
5096   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5097   PetscAssertPointer(depth, 2);
5098   if (mesh->tr) {
5099     PetscCall(DMPlexTransformGetDepth(mesh->tr, depth));
5100   } else {
5101     PetscCall(DMPlexGetDepthLabel(dm, &label));
5102     if (label) PetscCall(DMLabelGetNumValues(label, &d));
5103     *depth = d - 1;
5104   }
5105   PetscFunctionReturn(PETSC_SUCCESS);
5106 }
5107 
5108 /*@
5109   DMPlexGetDepthStratum - Get the bounds [`start`, `end`) for all points at a certain depth.
5110 
5111   Not Collective
5112 
5113   Input Parameters:
5114 + dm    - The `DMPLEX` object
5115 - depth - The requested depth
5116 
5117   Output Parameters:
5118 + start - The first point at this `depth`
5119 - end   - One beyond the last point at this `depth`
5120 
5121   Level: developer
5122 
5123   Notes:
5124   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
5125   often "vertices".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then depth stratum 1 contains the next
5126   higher dimension, e.g., "edges".
5127 
5128 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetHeightStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
5129 @*/
5130 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
5131 {
5132   DM_Plex *mesh = (DM_Plex *)dm->data;
5133   DMLabel  label;
5134   PetscInt pStart, pEnd;
5135 
5136   PetscFunctionBegin;
5137   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5138   if (start) {
5139     PetscAssertPointer(start, 3);
5140     *start = 0;
5141   }
5142   if (end) {
5143     PetscAssertPointer(end, 4);
5144     *end = 0;
5145   }
5146   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5147   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5148   if (depth < 0) {
5149     if (start) *start = pStart;
5150     if (end) *end = pEnd;
5151     PetscFunctionReturn(PETSC_SUCCESS);
5152   }
5153   if (mesh->tr) {
5154     PetscCall(DMPlexTransformGetDepthStratum(mesh->tr, depth, start, end));
5155   } else {
5156     PetscCall(DMPlexGetDepthLabel(dm, &label));
5157     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5158     PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
5159   }
5160   PetscFunctionReturn(PETSC_SUCCESS);
5161 }
5162 
5163 /*@
5164   DMPlexGetHeightStratum - Get the bounds [`start`, `end`) for all points at a certain height.
5165 
5166   Not Collective
5167 
5168   Input Parameters:
5169 + dm     - The `DMPLEX` object
5170 - height - The requested height
5171 
5172   Output Parameters:
5173 + start - The first point at this `height`
5174 - end   - One beyond the last point at this `height`
5175 
5176   Level: developer
5177 
5178   Notes:
5179   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
5180   points, often called "cells" or "elements".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then height
5181   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
5182 
5183 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5184 @*/
5185 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
5186 {
5187   DMLabel  label;
5188   PetscInt depth, pStart, pEnd;
5189 
5190   PetscFunctionBegin;
5191   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5192   if (start) {
5193     PetscAssertPointer(start, 3);
5194     *start = 0;
5195   }
5196   if (end) {
5197     PetscAssertPointer(end, 4);
5198     *end = 0;
5199   }
5200   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5201   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5202   if (height < 0) {
5203     if (start) *start = pStart;
5204     if (end) *end = pEnd;
5205     PetscFunctionReturn(PETSC_SUCCESS);
5206   }
5207   PetscCall(DMPlexGetDepthLabel(dm, &label));
5208   if (label) PetscCall(DMLabelGetNumValues(label, &depth));
5209   else PetscCall(DMGetDimension(dm, &depth));
5210   PetscCheck(depth >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Depth not yet computed");
5211   PetscCall(DMPlexGetDepthStratum(dm, depth - 1 - height, start, end));
5212   PetscFunctionReturn(PETSC_SUCCESS);
5213 }
5214 
5215 /*@
5216   DMPlexGetPointDepth - Get the `depth` of a given point
5217 
5218   Not Collective
5219 
5220   Input Parameters:
5221 + dm    - The `DMPLEX` object
5222 - point - The point
5223 
5224   Output Parameter:
5225 . depth - The depth of the `point`
5226 
5227   Level: intermediate
5228 
5229 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5230 @*/
5231 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
5232 {
5233   PetscFunctionBegin;
5234   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5235   PetscAssertPointer(depth, 3);
5236   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
5237   PetscFunctionReturn(PETSC_SUCCESS);
5238 }
5239 
5240 /*@
5241   DMPlexGetPointHeight - Get the `height` of a given point
5242 
5243   Not Collective
5244 
5245   Input Parameters:
5246 + dm    - The `DMPLEX` object
5247 - point - The point
5248 
5249   Output Parameter:
5250 . height - The height of the `point`
5251 
5252   Level: intermediate
5253 
5254 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
5255 @*/
5256 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5257 {
5258   PetscInt n, pDepth;
5259 
5260   PetscFunctionBegin;
5261   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5262   PetscAssertPointer(height, 3);
5263   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5264   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5265   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5266   PetscFunctionReturn(PETSC_SUCCESS);
5267 }
5268 
5269 /*@
5270   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5271 
5272   Not Collective
5273 
5274   Input Parameter:
5275 . dm - The `DMPLEX` object
5276 
5277   Output Parameter:
5278 . celltypeLabel - The `DMLabel` recording cell polytope type
5279 
5280   Level: developer
5281 
5282   Note:
5283   This function will trigger automatica computation of cell types. This can be disabled by calling
5284   `DMCreateLabel`(dm, "celltype") beforehand.
5285 
5286 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5287 @*/
5288 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5289 {
5290   PetscFunctionBegin;
5291   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5292   PetscAssertPointer(celltypeLabel, 2);
5293   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5294   *celltypeLabel = dm->celltypeLabel;
5295   PetscFunctionReturn(PETSC_SUCCESS);
5296 }
5297 
5298 /*@
5299   DMPlexGetCellType - Get the polytope type of a given cell
5300 
5301   Not Collective
5302 
5303   Input Parameters:
5304 + dm   - The `DMPLEX` object
5305 - cell - The cell
5306 
5307   Output Parameter:
5308 . celltype - The polytope type of the cell
5309 
5310   Level: intermediate
5311 
5312 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeType`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5313 @*/
5314 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5315 {
5316   DM_Plex *mesh = (DM_Plex *)dm->data;
5317   DMLabel  label;
5318   PetscInt ct;
5319 
5320   PetscFunctionBegin;
5321   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5322   PetscAssertPointer(celltype, 3);
5323   if (mesh->tr) {
5324     PetscCall(DMPlexTransformGetCellType(mesh->tr, cell, celltype));
5325   } else {
5326     PetscInt pStart, pEnd;
5327 
5328     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, NULL));
5329     if (!mesh->cellTypes) { /* XXX remove? optimize? */
5330       PetscCall(PetscSectionGetChart(mesh->coneSection, NULL, &pEnd));
5331       PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5332       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5333       for (PetscInt p = pStart; p < pEnd; p++) {
5334         PetscCall(DMLabelGetValue(label, p, &ct));
5335         mesh->cellTypes[p - pStart].value_as_uint8 = (DMPolytopeType)ct;
5336       }
5337     }
5338     *celltype = (DMPolytopeType)mesh->cellTypes[cell - pStart].value_as_uint8;
5339     if (PetscDefined(USE_DEBUG)) {
5340       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5341       PetscCall(DMLabelGetValue(label, cell, &ct));
5342       PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5343       PetscCheck(ct == (PetscInt)*celltype, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid cellType for %" PetscInt_FMT ": %d != %" PetscInt_FMT, cell, (int)*celltype, ct);
5344     }
5345   }
5346   PetscFunctionReturn(PETSC_SUCCESS);
5347 }
5348 
5349 /*@
5350   DMPlexSetCellType - Set the polytope type of a given cell
5351 
5352   Not Collective
5353 
5354   Input Parameters:
5355 + dm       - The `DMPLEX` object
5356 . cell     - The cell
5357 - celltype - The polytope type of the cell
5358 
5359   Level: advanced
5360 
5361   Note:
5362   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5363   is executed. This function will override the computed type. However, if automatic classification will not succeed
5364   and a user wants to manually specify all types, the classification must be disabled by calling
5365   DMCreateLabel(dm, "celltype") before getting or setting any cell types.
5366 
5367 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5368 @*/
5369 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5370 {
5371   DM_Plex *mesh = (DM_Plex *)dm->data;
5372   DMLabel  label;
5373   PetscInt pStart, pEnd;
5374 
5375   PetscFunctionBegin;
5376   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5377   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
5378   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5379   PetscCall(DMLabelSetValue(label, cell, celltype));
5380   if (!mesh->cellTypes) PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5381   mesh->cellTypes[cell - pStart].value_as_uint8 = celltype;
5382   PetscFunctionReturn(PETSC_SUCCESS);
5383 }
5384 
5385 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5386 {
5387   PetscSection section, s;
5388   Mat          m;
5389   PetscInt     maxHeight;
5390   const char  *prefix;
5391 
5392   PetscFunctionBegin;
5393   PetscCall(DMClone(dm, cdm));
5394   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5395   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5396   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5397   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5398   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5399   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5400   PetscCall(DMSetLocalSection(*cdm, section));
5401   PetscCall(PetscSectionDestroy(&section));
5402   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &s));
5403   PetscCall(MatCreate(PETSC_COMM_SELF, &m));
5404   PetscCall(DMSetDefaultConstraints(*cdm, s, m, NULL));
5405   PetscCall(PetscSectionDestroy(&s));
5406   PetscCall(MatDestroy(&m));
5407 
5408   PetscCall(DMSetNumFields(*cdm, 1));
5409   PetscCall(DMCreateDS(*cdm));
5410   (*cdm)->cloneOpts = PETSC_TRUE;
5411   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5412   PetscFunctionReturn(PETSC_SUCCESS);
5413 }
5414 
5415 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5416 {
5417   Vec coordsLocal, cellCoordsLocal;
5418   DM  coordsDM, cellCoordsDM;
5419 
5420   PetscFunctionBegin;
5421   *field = NULL;
5422   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5423   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5424   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5425   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5426   if (coordsLocal && coordsDM) {
5427     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5428     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5429   }
5430   PetscFunctionReturn(PETSC_SUCCESS);
5431 }
5432 
5433 /*@C
5434   DMPlexGetConeSection - Return a section which describes the layout of cone data
5435 
5436   Not Collective
5437 
5438   Input Parameter:
5439 . dm - The `DMPLEX` object
5440 
5441   Output Parameter:
5442 . section - The `PetscSection` object
5443 
5444   Level: developer
5445 
5446 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5447 @*/
5448 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5449 {
5450   DM_Plex *mesh = (DM_Plex *)dm->data;
5451 
5452   PetscFunctionBegin;
5453   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5454   if (section) *section = mesh->coneSection;
5455   PetscFunctionReturn(PETSC_SUCCESS);
5456 }
5457 
5458 /*@C
5459   DMPlexGetSupportSection - Return a section which describes the layout of support data
5460 
5461   Not Collective
5462 
5463   Input Parameter:
5464 . dm - The `DMPLEX` object
5465 
5466   Output Parameter:
5467 . section - The `PetscSection` object
5468 
5469   Level: developer
5470 
5471 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5472 @*/
5473 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5474 {
5475   DM_Plex *mesh = (DM_Plex *)dm->data;
5476 
5477   PetscFunctionBegin;
5478   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5479   if (section) *section = mesh->supportSection;
5480   PetscFunctionReturn(PETSC_SUCCESS);
5481 }
5482 
5483 /*@C
5484   DMPlexGetCones - Return cone data
5485 
5486   Not Collective
5487 
5488   Input Parameter:
5489 . dm - The `DMPLEX` object
5490 
5491   Output Parameter:
5492 . cones - The cone for each point
5493 
5494   Level: developer
5495 
5496 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5497 @*/
5498 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5499 {
5500   DM_Plex *mesh = (DM_Plex *)dm->data;
5501 
5502   PetscFunctionBegin;
5503   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5504   if (cones) *cones = mesh->cones;
5505   PetscFunctionReturn(PETSC_SUCCESS);
5506 }
5507 
5508 /*@C
5509   DMPlexGetConeOrientations - Return cone orientation data
5510 
5511   Not Collective
5512 
5513   Input Parameter:
5514 . dm - The `DMPLEX` object
5515 
5516   Output Parameter:
5517 . coneOrientations - The array of cone orientations for all points
5518 
5519   Level: developer
5520 
5521   Notes:
5522   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points as returned by `DMPlexGetConeOrientation()`.
5523 
5524   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5525 
5526 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5527 @*/
5528 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5529 {
5530   DM_Plex *mesh = (DM_Plex *)dm->data;
5531 
5532   PetscFunctionBegin;
5533   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5534   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5535   PetscFunctionReturn(PETSC_SUCCESS);
5536 }
5537 
5538 /******************************** FEM Support **********************************/
5539 
5540 PetscErrorCode DMPlexGetAllCells_Internal(DM plex, IS *cellIS)
5541 {
5542   PetscInt depth;
5543 
5544   PetscFunctionBegin;
5545   PetscCall(DMPlexGetDepth(plex, &depth));
5546   PetscCall(DMGetStratumIS(plex, "dim", depth, cellIS));
5547   if (!*cellIS) PetscCall(DMGetStratumIS(plex, "depth", depth, cellIS));
5548   PetscFunctionReturn(PETSC_SUCCESS);
5549 }
5550 
5551 /*
5552  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5553  representing a line in the section.
5554 */
5555 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(PetscSection section, PetscInt field, PetscInt line, PetscBool vertexchart, PetscInt *Nc, PetscInt *k)
5556 {
5557   PetscFunctionBeginHot;
5558   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5559   if (line < 0) {
5560     *k  = 0;
5561     *Nc = 0;
5562   } else if (vertexchart) { /* If we only have a vertex chart, we must have degree k=1 */
5563     *k = 1;
5564   } else { /* Assume the full interpolated mesh is in the chart; lines in particular */
5565     /* An order k SEM disc has k-1 dofs on an edge */
5566     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5567     *k = *k / *Nc + 1;
5568   }
5569   PetscFunctionReturn(PETSC_SUCCESS);
5570 }
5571 
5572 /*@
5573 
5574   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5575   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5576   section provided (or the section of the `DM`).
5577 
5578   Input Parameters:
5579 + dm      - The `DM`
5580 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or `PETSC_DETERMINE`
5581 - section - The `PetscSection` to reorder, or `NULL` for the default section
5582 
5583   Example:
5584   A typical interpolated single-quad mesh might order points as
5585 .vb
5586   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5587 
5588   v4 -- e6 -- v3
5589   |           |
5590   e7    c0    e8
5591   |           |
5592   v1 -- e5 -- v2
5593 .ve
5594 
5595   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5596   dofs in the order of points, e.g.,
5597 .vb
5598     c0 -> [0,1,2,3]
5599     v1 -> [4]
5600     ...
5601     e5 -> [8, 9]
5602 .ve
5603 
5604   which corresponds to the dofs
5605 .vb
5606     6   10  11  7
5607     13  2   3   15
5608     12  0   1   14
5609     4   8   9   5
5610 .ve
5611 
5612   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5613 .vb
5614   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5615 .ve
5616 
5617   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5618 .vb
5619    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5620 .ve
5621 
5622   Level: developer
5623 
5624   Notes:
5625   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5626   degree of the basis.
5627 
5628   This is required to run with libCEED.
5629 
5630 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5631 @*/
5632 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5633 {
5634   DMLabel   label;
5635   PetscInt  dim, depth = -1, eStart = -1, Nf;
5636   PetscBool vertexchart;
5637 
5638   PetscFunctionBegin;
5639   PetscCall(DMGetDimension(dm, &dim));
5640   if (dim < 1) PetscFunctionReturn(PETSC_SUCCESS);
5641   if (point < 0) {
5642     PetscInt sStart, sEnd;
5643 
5644     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5645     point = sEnd - sStart ? sStart : point;
5646   }
5647   PetscCall(DMPlexGetDepthLabel(dm, &label));
5648   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5649   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5650   if (depth == 1) {
5651     eStart = point;
5652   } else if (depth == dim) {
5653     const PetscInt *cone;
5654 
5655     PetscCall(DMPlexGetCone(dm, point, &cone));
5656     if (dim == 2) eStart = cone[0];
5657     else if (dim == 3) {
5658       const PetscInt *cone2;
5659       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5660       eStart = cone2[0];
5661     } 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);
5662   } 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);
5663   { /* Determine whether the chart covers all points or just vertices. */
5664     PetscInt pStart, pEnd, cStart, cEnd;
5665     PetscCall(DMPlexGetDepthStratum(dm, 0, &pStart, &pEnd));
5666     PetscCall(PetscSectionGetChart(section, &cStart, &cEnd));
5667     if (pStart == cStart && pEnd == cEnd) vertexchart = PETSC_TRUE;      /* Only vertices are in the chart */
5668     else if (cStart <= point && point < cEnd) vertexchart = PETSC_FALSE; /* Some interpolated points exist in the chart */
5669     else vertexchart = PETSC_TRUE;                                       /* Some interpolated points are not in chart; assume dofs only at cells and vertices */
5670   }
5671   PetscCall(PetscSectionGetNumFields(section, &Nf));
5672   for (PetscInt d = 1; d <= dim; d++) {
5673     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5674     PetscInt *perm;
5675 
5676     for (f = 0; f < Nf; ++f) {
5677       PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5678       size += PetscPowInt(k + 1, d) * Nc;
5679     }
5680     PetscCall(PetscMalloc1(size, &perm));
5681     for (f = 0; f < Nf; ++f) {
5682       switch (d) {
5683       case 1:
5684         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5685         /*
5686          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5687          We want              [ vtx0; edge of length k-1; vtx1 ]
5688          */
5689         for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
5690         for (i = 0; i < k - 1; i++)
5691           for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
5692         for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
5693         foffset = offset;
5694         break;
5695       case 2:
5696         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5697         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5698         /* The SEM order is
5699 
5700          v_lb, {e_b}, v_rb,
5701          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5702          v_lt, reverse {e_t}, v_rt
5703          */
5704         {
5705           const PetscInt of   = 0;
5706           const PetscInt oeb  = of + PetscSqr(k - 1);
5707           const PetscInt oer  = oeb + (k - 1);
5708           const PetscInt oet  = oer + (k - 1);
5709           const PetscInt oel  = oet + (k - 1);
5710           const PetscInt ovlb = oel + (k - 1);
5711           const PetscInt ovrb = ovlb + 1;
5712           const PetscInt ovrt = ovrb + 1;
5713           const PetscInt ovlt = ovrt + 1;
5714           PetscInt       o;
5715 
5716           /* bottom */
5717           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
5718           for (o = oeb; o < oer; ++o)
5719             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5720           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
5721           /* middle */
5722           for (i = 0; i < k - 1; ++i) {
5723             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
5724             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
5725               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5726             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
5727           }
5728           /* top */
5729           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
5730           for (o = oel - 1; o >= oet; --o)
5731             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5732           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
5733           foffset = offset;
5734         }
5735         break;
5736       case 3:
5737         /* The original hex closure is
5738 
5739          {c,
5740          f_b, f_t, f_f, f_b, f_r, f_l,
5741          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
5742          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
5743          */
5744         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5745         /* The SEM order is
5746          Bottom Slice
5747          v_blf, {e^{(k-1)-n}_bf}, v_brf,
5748          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
5749          v_blb, {e_bb}, v_brb,
5750 
5751          Middle Slice (j)
5752          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
5753          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
5754          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
5755 
5756          Top Slice
5757          v_tlf, {e_tf}, v_trf,
5758          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
5759          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
5760          */
5761         {
5762           const PetscInt oc    = 0;
5763           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
5764           const PetscInt oft   = ofb + PetscSqr(k - 1);
5765           const PetscInt off   = oft + PetscSqr(k - 1);
5766           const PetscInt ofk   = off + PetscSqr(k - 1);
5767           const PetscInt ofr   = ofk + PetscSqr(k - 1);
5768           const PetscInt ofl   = ofr + PetscSqr(k - 1);
5769           const PetscInt oebl  = ofl + PetscSqr(k - 1);
5770           const PetscInt oebb  = oebl + (k - 1);
5771           const PetscInt oebr  = oebb + (k - 1);
5772           const PetscInt oebf  = oebr + (k - 1);
5773           const PetscInt oetf  = oebf + (k - 1);
5774           const PetscInt oetr  = oetf + (k - 1);
5775           const PetscInt oetb  = oetr + (k - 1);
5776           const PetscInt oetl  = oetb + (k - 1);
5777           const PetscInt oerf  = oetl + (k - 1);
5778           const PetscInt oelf  = oerf + (k - 1);
5779           const PetscInt oelb  = oelf + (k - 1);
5780           const PetscInt oerb  = oelb + (k - 1);
5781           const PetscInt ovblf = oerb + (k - 1);
5782           const PetscInt ovblb = ovblf + 1;
5783           const PetscInt ovbrb = ovblb + 1;
5784           const PetscInt ovbrf = ovbrb + 1;
5785           const PetscInt ovtlf = ovbrf + 1;
5786           const PetscInt ovtrf = ovtlf + 1;
5787           const PetscInt ovtrb = ovtrf + 1;
5788           const PetscInt ovtlb = ovtrb + 1;
5789           PetscInt       o, n;
5790 
5791           /* Bottom Slice */
5792           /*   bottom */
5793           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
5794           for (o = oetf - 1; o >= oebf; --o)
5795             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5796           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
5797           /*   middle */
5798           for (i = 0; i < k - 1; ++i) {
5799             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
5800             for (n = 0; n < k - 1; ++n) {
5801               o = ofb + n * (k - 1) + i;
5802               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5803             }
5804             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
5805           }
5806           /*   top */
5807           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
5808           for (o = oebb; o < oebr; ++o)
5809             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5810           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
5811 
5812           /* Middle Slice */
5813           for (j = 0; j < k - 1; ++j) {
5814             /*   bottom */
5815             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
5816             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
5817               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5818             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
5819             /*   middle */
5820             for (i = 0; i < k - 1; ++i) {
5821               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
5822               for (n = 0; n < k - 1; ++n)
5823                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
5824               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
5825             }
5826             /*   top */
5827             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
5828             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
5829               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5830             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
5831           }
5832 
5833           /* Top Slice */
5834           /*   bottom */
5835           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
5836           for (o = oetf; o < oetr; ++o)
5837             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5838           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
5839           /*   middle */
5840           for (i = 0; i < k - 1; ++i) {
5841             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
5842             for (n = 0; n < k - 1; ++n)
5843               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
5844             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
5845           }
5846           /*   top */
5847           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
5848           for (o = oetl - 1; o >= oetb; --o)
5849             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5850           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
5851 
5852           foffset = offset;
5853         }
5854         break;
5855       default:
5856         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
5857       }
5858     }
5859     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
5860     /* Check permutation */
5861     {
5862       PetscInt *check;
5863 
5864       PetscCall(PetscMalloc1(size, &check));
5865       for (i = 0; i < size; ++i) {
5866         check[i] = -1;
5867         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
5868       }
5869       for (i = 0; i < size; ++i) check[perm[i]] = i;
5870       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
5871       PetscCall(PetscFree(check));
5872     }
5873     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
5874     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
5875       PetscInt *loc_perm;
5876       PetscCall(PetscMalloc1(size * 2, &loc_perm));
5877       for (PetscInt i = 0; i < size; i++) {
5878         loc_perm[i]        = perm[i];
5879         loc_perm[size + i] = size + perm[i];
5880       }
5881       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
5882     }
5883   }
5884   PetscFunctionReturn(PETSC_SUCCESS);
5885 }
5886 
5887 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
5888 {
5889   PetscDS  prob;
5890   PetscInt depth, Nf, h;
5891   DMLabel  label;
5892 
5893   PetscFunctionBeginHot;
5894   PetscCall(DMGetDS(dm, &prob));
5895   Nf      = prob->Nf;
5896   label   = dm->depthLabel;
5897   *dspace = NULL;
5898   if (field < Nf) {
5899     PetscObject disc = prob->disc[field];
5900 
5901     if (disc->classid == PETSCFE_CLASSID) {
5902       PetscDualSpace dsp;
5903 
5904       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
5905       PetscCall(DMLabelGetNumValues(label, &depth));
5906       PetscCall(DMLabelGetValue(label, point, &h));
5907       h = depth - 1 - h;
5908       if (h) {
5909         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
5910       } else {
5911         *dspace = dsp;
5912       }
5913     }
5914   }
5915   PetscFunctionReturn(PETSC_SUCCESS);
5916 }
5917 
5918 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5919 {
5920   PetscScalar       *array;
5921   const PetscScalar *vArray;
5922   const PetscInt    *cone, *coneO;
5923   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
5924 
5925   PetscFunctionBeginHot;
5926   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5927   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
5928   PetscCall(DMPlexGetCone(dm, point, &cone));
5929   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
5930   if (!values || !*values) {
5931     if ((point >= pStart) && (point < pEnd)) {
5932       PetscInt dof;
5933 
5934       PetscCall(PetscSectionGetDof(section, point, &dof));
5935       size += dof;
5936     }
5937     for (p = 0; p < numPoints; ++p) {
5938       const PetscInt cp = cone[p];
5939       PetscInt       dof;
5940 
5941       if ((cp < pStart) || (cp >= pEnd)) continue;
5942       PetscCall(PetscSectionGetDof(section, cp, &dof));
5943       size += dof;
5944     }
5945     if (!values) {
5946       if (csize) *csize = size;
5947       PetscFunctionReturn(PETSC_SUCCESS);
5948     }
5949     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
5950   } else {
5951     array = *values;
5952   }
5953   size = 0;
5954   PetscCall(VecGetArrayRead(v, &vArray));
5955   if ((point >= pStart) && (point < pEnd)) {
5956     PetscInt           dof, off, d;
5957     const PetscScalar *varr;
5958 
5959     PetscCall(PetscSectionGetDof(section, point, &dof));
5960     PetscCall(PetscSectionGetOffset(section, point, &off));
5961     varr = &vArray[off];
5962     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
5963     size += dof;
5964   }
5965   for (p = 0; p < numPoints; ++p) {
5966     const PetscInt     cp = cone[p];
5967     PetscInt           o  = coneO[p];
5968     PetscInt           dof, off, d;
5969     const PetscScalar *varr;
5970 
5971     if ((cp < pStart) || (cp >= pEnd)) continue;
5972     PetscCall(PetscSectionGetDof(section, cp, &dof));
5973     PetscCall(PetscSectionGetOffset(section, cp, &off));
5974     varr = &vArray[off];
5975     if (o >= 0) {
5976       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
5977     } else {
5978       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
5979     }
5980     size += dof;
5981   }
5982   PetscCall(VecRestoreArrayRead(v, &vArray));
5983   if (!*values) {
5984     if (csize) *csize = size;
5985     *values = array;
5986   } else {
5987     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
5988     *csize = size;
5989   }
5990   PetscFunctionReturn(PETSC_SUCCESS);
5991 }
5992 
5993 /* Compress out points not in the section */
5994 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
5995 {
5996   const PetscInt np = *numPoints;
5997   PetscInt       pStart, pEnd, p, q;
5998 
5999   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6000   for (p = 0, q = 0; p < np; ++p) {
6001     const PetscInt r = points[p * 2];
6002     if ((r >= pStart) && (r < pEnd)) {
6003       points[q * 2]     = r;
6004       points[q * 2 + 1] = points[p * 2 + 1];
6005       ++q;
6006     }
6007   }
6008   *numPoints = q;
6009   return PETSC_SUCCESS;
6010 }
6011 
6012 /* Compressed closure does not apply closure permutation */
6013 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt ornt, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6014 {
6015   const PetscInt *cla = NULL;
6016   PetscInt        np, *pts = NULL;
6017 
6018   PetscFunctionBeginHot;
6019   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
6020   if (!ornt && *clPoints) {
6021     PetscInt dof, off;
6022 
6023     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
6024     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
6025     PetscCall(ISGetIndices(*clPoints, &cla));
6026     np  = dof / 2;
6027     pts = (PetscInt *)&cla[off];
6028   } else {
6029     PetscCall(DMPlexGetTransitiveClosure_Internal(dm, point, ornt, PETSC_TRUE, &np, &pts));
6030     PetscCall(CompressPoints_Private(section, &np, pts));
6031   }
6032   *numPoints = np;
6033   *points    = pts;
6034   *clp       = cla;
6035   PetscFunctionReturn(PETSC_SUCCESS);
6036 }
6037 
6038 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6039 {
6040   PetscFunctionBeginHot;
6041   if (!*clPoints) {
6042     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
6043   } else {
6044     PetscCall(ISRestoreIndices(*clPoints, clp));
6045   }
6046   *numPoints = 0;
6047   *points    = NULL;
6048   *clSec     = NULL;
6049   *clPoints  = NULL;
6050   *clp       = NULL;
6051   PetscFunctionReturn(PETSC_SUCCESS);
6052 }
6053 
6054 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
6055 {
6056   PetscInt            offset = 0, p;
6057   const PetscInt    **perms  = NULL;
6058   const PetscScalar **flips  = NULL;
6059 
6060   PetscFunctionBeginHot;
6061   *size = 0;
6062   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6063   for (p = 0; p < numPoints; p++) {
6064     const PetscInt     point = points[2 * p];
6065     const PetscInt    *perm  = perms ? perms[p] : NULL;
6066     const PetscScalar *flip  = flips ? flips[p] : NULL;
6067     PetscInt           dof, off, d;
6068     const PetscScalar *varr;
6069 
6070     PetscCall(PetscSectionGetDof(section, point, &dof));
6071     PetscCall(PetscSectionGetOffset(section, point, &off));
6072     varr = &vArray[off];
6073     if (clperm) {
6074       if (perm) {
6075         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
6076       } else {
6077         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
6078       }
6079       if (flip) {
6080         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
6081       }
6082     } else {
6083       if (perm) {
6084         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
6085       } else {
6086         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
6087       }
6088       if (flip) {
6089         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
6090       }
6091     }
6092     offset += dof;
6093   }
6094   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6095   *size = offset;
6096   PetscFunctionReturn(PETSC_SUCCESS);
6097 }
6098 
6099 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[])
6100 {
6101   PetscInt offset = 0, f;
6102 
6103   PetscFunctionBeginHot;
6104   *size = 0;
6105   for (f = 0; f < numFields; ++f) {
6106     PetscInt            p;
6107     const PetscInt    **perms = NULL;
6108     const PetscScalar **flips = NULL;
6109 
6110     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6111     for (p = 0; p < numPoints; p++) {
6112       const PetscInt     point = points[2 * p];
6113       PetscInt           fdof, foff, b;
6114       const PetscScalar *varr;
6115       const PetscInt    *perm = perms ? perms[p] : NULL;
6116       const PetscScalar *flip = flips ? flips[p] : NULL;
6117 
6118       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6119       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6120       varr = &vArray[foff];
6121       if (clperm) {
6122         if (perm) {
6123           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
6124         } else {
6125           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
6126         }
6127         if (flip) {
6128           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
6129         }
6130       } else {
6131         if (perm) {
6132           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
6133         } else {
6134           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
6135         }
6136         if (flip) {
6137           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
6138         }
6139       }
6140       offset += fdof;
6141     }
6142     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6143   }
6144   *size = offset;
6145   PetscFunctionReturn(PETSC_SUCCESS);
6146 }
6147 
6148 PetscErrorCode DMPlexVecGetOrientedClosure_Internal(DM dm, PetscSection section, PetscBool useClPerm, Vec v, PetscInt point, PetscInt ornt, PetscInt *csize, PetscScalar *values[])
6149 {
6150   PetscSection    clSection;
6151   IS              clPoints;
6152   PetscInt       *points = NULL;
6153   const PetscInt *clp, *perm = NULL;
6154   PetscInt        depth, numFields, numPoints, asize;
6155 
6156   PetscFunctionBeginHot;
6157   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6158   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6159   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6160   PetscValidHeaderSpecific(v, VEC_CLASSID, 4);
6161   PetscCall(DMPlexGetDepth(dm, &depth));
6162   PetscCall(PetscSectionGetNumFields(section, &numFields));
6163   if (depth == 1 && numFields < 2) {
6164     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6165     PetscFunctionReturn(PETSC_SUCCESS);
6166   }
6167   /* Get points */
6168   PetscCall(DMPlexGetCompressedClosure(dm, section, point, ornt, &numPoints, &points, &clSection, &clPoints, &clp));
6169   /* Get sizes */
6170   asize = 0;
6171   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6172     PetscInt dof;
6173     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6174     asize += dof;
6175   }
6176   if (values) {
6177     const PetscScalar *vArray;
6178     PetscInt           size;
6179 
6180     if (*values) {
6181       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);
6182     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6183     if (useClPerm) PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6184     PetscCall(VecGetArrayRead(v, &vArray));
6185     /* Get values */
6186     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6187     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6188     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6189     /* Cleanup array */
6190     PetscCall(VecRestoreArrayRead(v, &vArray));
6191   }
6192   if (csize) *csize = asize;
6193   /* Cleanup points */
6194   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6195   PetscFunctionReturn(PETSC_SUCCESS);
6196 }
6197 
6198 /*@C
6199   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
6200 
6201   Not collective
6202 
6203   Input Parameters:
6204 + dm      - The `DM`
6205 . section - The section describing the layout in `v`, or `NULL` to use the default section
6206 . v       - The local vector
6207 - point   - The point in the `DM`
6208 
6209   Input/Output Parameters:
6210 + csize  - The size of the input values array, or `NULL`; on output the number of values in the closure
6211 - values - An array to use for the values, or `NULL` to have it allocated automatically;
6212            if the user provided `NULL`, it is a borrowed array and should not be freed
6213 
6214   Level: intermediate
6215 
6216   Notes:
6217   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6218   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
6219   assembly function, and a user may already have allocated storage for this operation.
6220 
6221   A typical use could be
6222 .vb
6223    values = NULL;
6224    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6225    for (cl = 0; cl < clSize; ++cl) {
6226      <Compute on closure>
6227    }
6228    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
6229 .ve
6230   or
6231 .vb
6232    PetscMalloc1(clMaxSize, &values);
6233    for (p = pStart; p < pEnd; ++p) {
6234      clSize = clMaxSize;
6235      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6236      for (cl = 0; cl < clSize; ++cl) {
6237        <Compute on closure>
6238      }
6239    }
6240    PetscFree(values);
6241 .ve
6242 
6243   Fortran Notes:
6244   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6245 
6246 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6247 @*/
6248 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6249 {
6250   PetscFunctionBeginHot;
6251   PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, section, PETSC_TRUE, v, point, 0, csize, values));
6252   PetscFunctionReturn(PETSC_SUCCESS);
6253 }
6254 
6255 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6256 {
6257   DMLabel            depthLabel;
6258   PetscSection       clSection;
6259   IS                 clPoints;
6260   PetscScalar       *array;
6261   const PetscScalar *vArray;
6262   PetscInt          *points = NULL;
6263   const PetscInt    *clp, *perm = NULL;
6264   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6265 
6266   PetscFunctionBeginHot;
6267   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6268   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6269   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6270   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6271   PetscCall(DMPlexGetDepth(dm, &mdepth));
6272   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6273   PetscCall(PetscSectionGetNumFields(section, &numFields));
6274   if (mdepth == 1 && numFields < 2) {
6275     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6276     PetscFunctionReturn(PETSC_SUCCESS);
6277   }
6278   /* Get points */
6279   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6280   for (clsize = 0, p = 0; p < Np; p++) {
6281     PetscInt dof;
6282     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6283     clsize += dof;
6284   }
6285   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6286   /* Filter points */
6287   for (p = 0; p < numPoints * 2; p += 2) {
6288     PetscInt dep;
6289 
6290     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6291     if (dep != depth) continue;
6292     points[Np * 2 + 0] = points[p];
6293     points[Np * 2 + 1] = points[p + 1];
6294     ++Np;
6295   }
6296   /* Get array */
6297   if (!values || !*values) {
6298     PetscInt asize = 0, dof;
6299 
6300     for (p = 0; p < Np * 2; p += 2) {
6301       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6302       asize += dof;
6303     }
6304     if (!values) {
6305       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6306       if (csize) *csize = asize;
6307       PetscFunctionReturn(PETSC_SUCCESS);
6308     }
6309     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6310   } else {
6311     array = *values;
6312   }
6313   PetscCall(VecGetArrayRead(v, &vArray));
6314   /* Get values */
6315   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6316   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6317   /* Cleanup points */
6318   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6319   /* Cleanup array */
6320   PetscCall(VecRestoreArrayRead(v, &vArray));
6321   if (!*values) {
6322     if (csize) *csize = size;
6323     *values = array;
6324   } else {
6325     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6326     *csize = size;
6327   }
6328   PetscFunctionReturn(PETSC_SUCCESS);
6329 }
6330 
6331 /*@C
6332   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point'
6333 
6334   Not collective
6335 
6336   Input Parameters:
6337 + dm      - The `DM`
6338 . section - The section describing the layout in `v`, or `NULL` to use the default section
6339 . v       - The local vector
6340 . point   - The point in the `DM`
6341 . csize   - The number of values in the closure, or `NULL`
6342 - values  - The array of values, which is a borrowed array and should not be freed
6343 
6344   Level: intermediate
6345 
6346   Note:
6347   The array values are discarded and not copied back into `v`. In order to copy values back to `v`, use `DMPlexVecSetClosure()`
6348 
6349   Fortran Notes:
6350   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6351 
6352 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6353 @*/
6354 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6355 {
6356   PetscInt size = 0;
6357 
6358   PetscFunctionBegin;
6359   /* Should work without recalculating size */
6360   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6361   *values = NULL;
6362   PetscFunctionReturn(PETSC_SUCCESS);
6363 }
6364 
6365 static inline void add(PetscScalar *x, PetscScalar y)
6366 {
6367   *x += y;
6368 }
6369 static inline void insert(PetscScalar *x, PetscScalar y)
6370 {
6371   *x = y;
6372 }
6373 
6374 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[])
6375 {
6376   PetscInt        cdof;  /* The number of constraints on this point */
6377   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6378   PetscScalar    *a;
6379   PetscInt        off, cind = 0, k;
6380 
6381   PetscFunctionBegin;
6382   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6383   PetscCall(PetscSectionGetOffset(section, point, &off));
6384   a = &array[off];
6385   if (!cdof || setBC) {
6386     if (clperm) {
6387       if (perm) {
6388         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6389       } else {
6390         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6391       }
6392     } else {
6393       if (perm) {
6394         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6395       } else {
6396         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6397       }
6398     }
6399   } else {
6400     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6401     if (clperm) {
6402       if (perm) {
6403         for (k = 0; k < dof; ++k) {
6404           if ((cind < cdof) && (k == cdofs[cind])) {
6405             ++cind;
6406             continue;
6407           }
6408           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6409         }
6410       } else {
6411         for (k = 0; k < dof; ++k) {
6412           if ((cind < cdof) && (k == cdofs[cind])) {
6413             ++cind;
6414             continue;
6415           }
6416           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6417         }
6418       }
6419     } else {
6420       if (perm) {
6421         for (k = 0; k < dof; ++k) {
6422           if ((cind < cdof) && (k == cdofs[cind])) {
6423             ++cind;
6424             continue;
6425           }
6426           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6427         }
6428       } else {
6429         for (k = 0; k < dof; ++k) {
6430           if ((cind < cdof) && (k == cdofs[cind])) {
6431             ++cind;
6432             continue;
6433           }
6434           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6435         }
6436       }
6437     }
6438   }
6439   PetscFunctionReturn(PETSC_SUCCESS);
6440 }
6441 
6442 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[])
6443 {
6444   PetscInt        cdof;  /* The number of constraints on this point */
6445   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6446   PetscScalar    *a;
6447   PetscInt        off, cind = 0, k;
6448 
6449   PetscFunctionBegin;
6450   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6451   PetscCall(PetscSectionGetOffset(section, point, &off));
6452   a = &array[off];
6453   if (cdof) {
6454     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6455     if (clperm) {
6456       if (perm) {
6457         for (k = 0; k < dof; ++k) {
6458           if ((cind < cdof) && (k == cdofs[cind])) {
6459             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6460             cind++;
6461           }
6462         }
6463       } else {
6464         for (k = 0; k < dof; ++k) {
6465           if ((cind < cdof) && (k == cdofs[cind])) {
6466             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6467             cind++;
6468           }
6469         }
6470       }
6471     } else {
6472       if (perm) {
6473         for (k = 0; k < dof; ++k) {
6474           if ((cind < cdof) && (k == cdofs[cind])) {
6475             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6476             cind++;
6477           }
6478         }
6479       } else {
6480         for (k = 0; k < dof; ++k) {
6481           if ((cind < cdof) && (k == cdofs[cind])) {
6482             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6483             cind++;
6484           }
6485         }
6486       }
6487     }
6488   }
6489   PetscFunctionReturn(PETSC_SUCCESS);
6490 }
6491 
6492 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[])
6493 {
6494   PetscScalar    *a;
6495   PetscInt        fdof, foff, fcdof, foffset = *offset;
6496   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6497   PetscInt        cind = 0, b;
6498 
6499   PetscFunctionBegin;
6500   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6501   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6502   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6503   a = &array[foff];
6504   if (!fcdof || setBC) {
6505     if (clperm) {
6506       if (perm) {
6507         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6508       } else {
6509         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6510       }
6511     } else {
6512       if (perm) {
6513         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6514       } else {
6515         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6516       }
6517     }
6518   } else {
6519     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6520     if (clperm) {
6521       if (perm) {
6522         for (b = 0; b < fdof; b++) {
6523           if ((cind < fcdof) && (b == fcdofs[cind])) {
6524             ++cind;
6525             continue;
6526           }
6527           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6528         }
6529       } else {
6530         for (b = 0; b < fdof; b++) {
6531           if ((cind < fcdof) && (b == fcdofs[cind])) {
6532             ++cind;
6533             continue;
6534           }
6535           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6536         }
6537       }
6538     } else {
6539       if (perm) {
6540         for (b = 0; b < fdof; b++) {
6541           if ((cind < fcdof) && (b == fcdofs[cind])) {
6542             ++cind;
6543             continue;
6544           }
6545           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6546         }
6547       } else {
6548         for (b = 0; b < fdof; b++) {
6549           if ((cind < fcdof) && (b == fcdofs[cind])) {
6550             ++cind;
6551             continue;
6552           }
6553           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6554         }
6555       }
6556     }
6557   }
6558   *offset += fdof;
6559   PetscFunctionReturn(PETSC_SUCCESS);
6560 }
6561 
6562 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[])
6563 {
6564   PetscScalar    *a;
6565   PetscInt        fdof, foff, fcdof, foffset = *offset;
6566   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6567   PetscInt        Nc, cind = 0, ncind = 0, b;
6568   PetscBool       ncSet, fcSet;
6569 
6570   PetscFunctionBegin;
6571   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6572   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6573   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6574   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6575   a = &array[foff];
6576   if (fcdof) {
6577     /* We just override fcdof and fcdofs with Ncc and comps */
6578     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6579     if (clperm) {
6580       if (perm) {
6581         if (comps) {
6582           for (b = 0; b < fdof; b++) {
6583             ncSet = fcSet = PETSC_FALSE;
6584             if (b % Nc == comps[ncind]) {
6585               ncind = (ncind + 1) % Ncc;
6586               ncSet = PETSC_TRUE;
6587             }
6588             if ((cind < fcdof) && (b == fcdofs[cind])) {
6589               ++cind;
6590               fcSet = PETSC_TRUE;
6591             }
6592             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6593           }
6594         } else {
6595           for (b = 0; b < fdof; b++) {
6596             if ((cind < fcdof) && (b == fcdofs[cind])) {
6597               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6598               ++cind;
6599             }
6600           }
6601         }
6602       } else {
6603         if (comps) {
6604           for (b = 0; b < fdof; b++) {
6605             ncSet = fcSet = PETSC_FALSE;
6606             if (b % Nc == comps[ncind]) {
6607               ncind = (ncind + 1) % Ncc;
6608               ncSet = PETSC_TRUE;
6609             }
6610             if ((cind < fcdof) && (b == fcdofs[cind])) {
6611               ++cind;
6612               fcSet = PETSC_TRUE;
6613             }
6614             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6615           }
6616         } else {
6617           for (b = 0; b < fdof; b++) {
6618             if ((cind < fcdof) && (b == fcdofs[cind])) {
6619               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6620               ++cind;
6621             }
6622           }
6623         }
6624       }
6625     } else {
6626       if (perm) {
6627         if (comps) {
6628           for (b = 0; b < fdof; b++) {
6629             ncSet = fcSet = PETSC_FALSE;
6630             if (b % Nc == comps[ncind]) {
6631               ncind = (ncind + 1) % Ncc;
6632               ncSet = PETSC_TRUE;
6633             }
6634             if ((cind < fcdof) && (b == fcdofs[cind])) {
6635               ++cind;
6636               fcSet = PETSC_TRUE;
6637             }
6638             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6639           }
6640         } else {
6641           for (b = 0; b < fdof; b++) {
6642             if ((cind < fcdof) && (b == fcdofs[cind])) {
6643               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6644               ++cind;
6645             }
6646           }
6647         }
6648       } else {
6649         if (comps) {
6650           for (b = 0; b < fdof; b++) {
6651             ncSet = fcSet = PETSC_FALSE;
6652             if (b % Nc == comps[ncind]) {
6653               ncind = (ncind + 1) % Ncc;
6654               ncSet = PETSC_TRUE;
6655             }
6656             if ((cind < fcdof) && (b == fcdofs[cind])) {
6657               ++cind;
6658               fcSet = PETSC_TRUE;
6659             }
6660             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6661           }
6662         } else {
6663           for (b = 0; b < fdof; b++) {
6664             if ((cind < fcdof) && (b == fcdofs[cind])) {
6665               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6666               ++cind;
6667             }
6668           }
6669         }
6670       }
6671     }
6672   }
6673   *offset += fdof;
6674   PetscFunctionReturn(PETSC_SUCCESS);
6675 }
6676 
6677 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6678 {
6679   PetscScalar    *array;
6680   const PetscInt *cone, *coneO;
6681   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6682 
6683   PetscFunctionBeginHot;
6684   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6685   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6686   PetscCall(DMPlexGetCone(dm, point, &cone));
6687   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6688   PetscCall(VecGetArray(v, &array));
6689   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6690     const PetscInt cp = !p ? point : cone[p - 1];
6691     const PetscInt o  = !p ? 0 : coneO[p - 1];
6692 
6693     if ((cp < pStart) || (cp >= pEnd)) {
6694       dof = 0;
6695       continue;
6696     }
6697     PetscCall(PetscSectionGetDof(section, cp, &dof));
6698     /* ADD_VALUES */
6699     {
6700       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6701       PetscScalar    *a;
6702       PetscInt        cdof, coff, cind = 0, k;
6703 
6704       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
6705       PetscCall(PetscSectionGetOffset(section, cp, &coff));
6706       a = &array[coff];
6707       if (!cdof) {
6708         if (o >= 0) {
6709           for (k = 0; k < dof; ++k) a[k] += values[off + k];
6710         } else {
6711           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
6712         }
6713       } else {
6714         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
6715         if (o >= 0) {
6716           for (k = 0; k < dof; ++k) {
6717             if ((cind < cdof) && (k == cdofs[cind])) {
6718               ++cind;
6719               continue;
6720             }
6721             a[k] += values[off + k];
6722           }
6723         } else {
6724           for (k = 0; k < dof; ++k) {
6725             if ((cind < cdof) && (k == cdofs[cind])) {
6726               ++cind;
6727               continue;
6728             }
6729             a[k] += values[off + dof - k - 1];
6730           }
6731         }
6732       }
6733     }
6734   }
6735   PetscCall(VecRestoreArray(v, &array));
6736   PetscFunctionReturn(PETSC_SUCCESS);
6737 }
6738 
6739 /*@C
6740   DMPlexVecSetClosure - Set an array of the values on the closure of `point`
6741 
6742   Not collective
6743 
6744   Input Parameters:
6745 + dm      - The `DM`
6746 . section - The section describing the layout in `v`, or `NULL` to use the default section
6747 . v       - The local vector
6748 . point   - The point in the `DM`
6749 . values  - The array of values
6750 - mode    - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
6751          where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
6752 
6753   Level: intermediate
6754 
6755 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
6756 @*/
6757 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6758 {
6759   PetscSection    clSection;
6760   IS              clPoints;
6761   PetscScalar    *array;
6762   PetscInt       *points = NULL;
6763   const PetscInt *clp, *clperm = NULL;
6764   PetscInt        depth, numFields, numPoints, p, clsize;
6765 
6766   PetscFunctionBeginHot;
6767   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6768   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6769   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6770   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6771   PetscCall(DMPlexGetDepth(dm, &depth));
6772   PetscCall(PetscSectionGetNumFields(section, &numFields));
6773   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
6774     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
6775     PetscFunctionReturn(PETSC_SUCCESS);
6776   }
6777   /* Get points */
6778   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6779   for (clsize = 0, p = 0; p < numPoints; p++) {
6780     PetscInt dof;
6781     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6782     clsize += dof;
6783   }
6784   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
6785   /* Get array */
6786   PetscCall(VecGetArray(v, &array));
6787   /* Get values */
6788   if (numFields > 0) {
6789     PetscInt offset = 0, f;
6790     for (f = 0; f < numFields; ++f) {
6791       const PetscInt    **perms = NULL;
6792       const PetscScalar **flips = NULL;
6793 
6794       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6795       switch (mode) {
6796       case INSERT_VALUES:
6797         for (p = 0; p < numPoints; p++) {
6798           const PetscInt     point = points[2 * p];
6799           const PetscInt    *perm  = perms ? perms[p] : NULL;
6800           const PetscScalar *flip  = flips ? flips[p] : NULL;
6801           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array));
6802         }
6803         break;
6804       case INSERT_ALL_VALUES:
6805         for (p = 0; p < numPoints; p++) {
6806           const PetscInt     point = points[2 * p];
6807           const PetscInt    *perm  = perms ? perms[p] : NULL;
6808           const PetscScalar *flip  = flips ? flips[p] : NULL;
6809           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array));
6810         }
6811         break;
6812       case INSERT_BC_VALUES:
6813         for (p = 0; p < numPoints; p++) {
6814           const PetscInt     point = points[2 * p];
6815           const PetscInt    *perm  = perms ? perms[p] : NULL;
6816           const PetscScalar *flip  = flips ? flips[p] : NULL;
6817           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array));
6818         }
6819         break;
6820       case ADD_VALUES:
6821         for (p = 0; p < numPoints; p++) {
6822           const PetscInt     point = points[2 * p];
6823           const PetscInt    *perm  = perms ? perms[p] : NULL;
6824           const PetscScalar *flip  = flips ? flips[p] : NULL;
6825           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array));
6826         }
6827         break;
6828       case ADD_ALL_VALUES:
6829         for (p = 0; p < numPoints; p++) {
6830           const PetscInt     point = points[2 * p];
6831           const PetscInt    *perm  = perms ? perms[p] : NULL;
6832           const PetscScalar *flip  = flips ? flips[p] : NULL;
6833           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array));
6834         }
6835         break;
6836       case ADD_BC_VALUES:
6837         for (p = 0; p < numPoints; p++) {
6838           const PetscInt     point = points[2 * p];
6839           const PetscInt    *perm  = perms ? perms[p] : NULL;
6840           const PetscScalar *flip  = flips ? flips[p] : NULL;
6841           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array));
6842         }
6843         break;
6844       default:
6845         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6846       }
6847       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6848     }
6849   } else {
6850     PetscInt            dof, off;
6851     const PetscInt    **perms = NULL;
6852     const PetscScalar **flips = NULL;
6853 
6854     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6855     switch (mode) {
6856     case INSERT_VALUES:
6857       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6858         const PetscInt     point = points[2 * p];
6859         const PetscInt    *perm  = perms ? perms[p] : NULL;
6860         const PetscScalar *flip  = flips ? flips[p] : NULL;
6861         PetscCall(PetscSectionGetDof(section, point, &dof));
6862         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array));
6863       }
6864       break;
6865     case INSERT_ALL_VALUES:
6866       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6867         const PetscInt     point = points[2 * p];
6868         const PetscInt    *perm  = perms ? perms[p] : NULL;
6869         const PetscScalar *flip  = flips ? flips[p] : NULL;
6870         PetscCall(PetscSectionGetDof(section, point, &dof));
6871         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array));
6872       }
6873       break;
6874     case INSERT_BC_VALUES:
6875       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6876         const PetscInt     point = points[2 * p];
6877         const PetscInt    *perm  = perms ? perms[p] : NULL;
6878         const PetscScalar *flip  = flips ? flips[p] : NULL;
6879         PetscCall(PetscSectionGetDof(section, point, &dof));
6880         PetscCall(updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array));
6881       }
6882       break;
6883     case ADD_VALUES:
6884       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6885         const PetscInt     point = points[2 * p];
6886         const PetscInt    *perm  = perms ? perms[p] : NULL;
6887         const PetscScalar *flip  = flips ? flips[p] : NULL;
6888         PetscCall(PetscSectionGetDof(section, point, &dof));
6889         PetscCall(updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array));
6890       }
6891       break;
6892     case ADD_ALL_VALUES:
6893       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6894         const PetscInt     point = points[2 * p];
6895         const PetscInt    *perm  = perms ? perms[p] : NULL;
6896         const PetscScalar *flip  = flips ? flips[p] : NULL;
6897         PetscCall(PetscSectionGetDof(section, point, &dof));
6898         PetscCall(updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array));
6899       }
6900       break;
6901     case ADD_BC_VALUES:
6902       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6903         const PetscInt     point = points[2 * p];
6904         const PetscInt    *perm  = perms ? perms[p] : NULL;
6905         const PetscScalar *flip  = flips ? flips[p] : NULL;
6906         PetscCall(PetscSectionGetDof(section, point, &dof));
6907         PetscCall(updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array));
6908       }
6909       break;
6910     default:
6911       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6912     }
6913     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6914   }
6915   /* Cleanup points */
6916   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6917   /* Cleanup array */
6918   PetscCall(VecRestoreArray(v, &array));
6919   PetscFunctionReturn(PETSC_SUCCESS);
6920 }
6921 
6922 /* Check whether the given point is in the label. If not, update the offset to skip this point */
6923 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
6924 {
6925   PetscFunctionBegin;
6926   *contains = PETSC_TRUE;
6927   if (label) {
6928     PetscInt fdof;
6929 
6930     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
6931     if (!*contains) {
6932       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6933       *offset += fdof;
6934       PetscFunctionReturn(PETSC_SUCCESS);
6935     }
6936   }
6937   PetscFunctionReturn(PETSC_SUCCESS);
6938 }
6939 
6940 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
6941 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)
6942 {
6943   PetscSection    clSection;
6944   IS              clPoints;
6945   PetscScalar    *array;
6946   PetscInt       *points = NULL;
6947   const PetscInt *clp;
6948   PetscInt        numFields, numPoints, p;
6949   PetscInt        offset = 0, f;
6950 
6951   PetscFunctionBeginHot;
6952   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6953   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6954   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6955   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6956   PetscCall(PetscSectionGetNumFields(section, &numFields));
6957   /* Get points */
6958   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6959   /* Get array */
6960   PetscCall(VecGetArray(v, &array));
6961   /* Get values */
6962   for (f = 0; f < numFields; ++f) {
6963     const PetscInt    **perms = NULL;
6964     const PetscScalar **flips = NULL;
6965     PetscBool           contains;
6966 
6967     if (!fieldActive[f]) {
6968       for (p = 0; p < numPoints * 2; p += 2) {
6969         PetscInt fdof;
6970         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
6971         offset += fdof;
6972       }
6973       continue;
6974     }
6975     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6976     switch (mode) {
6977     case INSERT_VALUES:
6978       for (p = 0; p < numPoints; p++) {
6979         const PetscInt     point = points[2 * p];
6980         const PetscInt    *perm  = perms ? perms[p] : NULL;
6981         const PetscScalar *flip  = flips ? flips[p] : NULL;
6982         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6983         if (!contains) continue;
6984         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
6985       }
6986       break;
6987     case INSERT_ALL_VALUES:
6988       for (p = 0; p < numPoints; p++) {
6989         const PetscInt     point = points[2 * p];
6990         const PetscInt    *perm  = perms ? perms[p] : NULL;
6991         const PetscScalar *flip  = flips ? flips[p] : NULL;
6992         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6993         if (!contains) continue;
6994         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
6995       }
6996       break;
6997     case INSERT_BC_VALUES:
6998       for (p = 0; p < numPoints; p++) {
6999         const PetscInt     point = points[2 * p];
7000         const PetscInt    *perm  = perms ? perms[p] : NULL;
7001         const PetscScalar *flip  = flips ? flips[p] : NULL;
7002         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7003         if (!contains) continue;
7004         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
7005       }
7006       break;
7007     case ADD_VALUES:
7008       for (p = 0; p < numPoints; p++) {
7009         const PetscInt     point = points[2 * p];
7010         const PetscInt    *perm  = perms ? perms[p] : NULL;
7011         const PetscScalar *flip  = flips ? flips[p] : NULL;
7012         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7013         if (!contains) continue;
7014         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
7015       }
7016       break;
7017     case ADD_ALL_VALUES:
7018       for (p = 0; p < numPoints; p++) {
7019         const PetscInt     point = points[2 * p];
7020         const PetscInt    *perm  = perms ? perms[p] : NULL;
7021         const PetscScalar *flip  = flips ? flips[p] : NULL;
7022         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7023         if (!contains) continue;
7024         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
7025       }
7026       break;
7027     default:
7028       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7029     }
7030     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7031   }
7032   /* Cleanup points */
7033   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7034   /* Cleanup array */
7035   PetscCall(VecRestoreArray(v, &array));
7036   PetscFunctionReturn(PETSC_SUCCESS);
7037 }
7038 
7039 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
7040 {
7041   PetscMPIInt rank;
7042   PetscInt    i, j;
7043 
7044   PetscFunctionBegin;
7045   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7046   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
7047   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
7048   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
7049   numCIndices = numCIndices ? numCIndices : numRIndices;
7050   if (!values) PetscFunctionReturn(PETSC_SUCCESS);
7051   for (i = 0; i < numRIndices; i++) {
7052     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
7053     for (j = 0; j < numCIndices; j++) {
7054 #if defined(PETSC_USE_COMPLEX)
7055       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
7056 #else
7057       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
7058 #endif
7059     }
7060     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
7061   }
7062   PetscFunctionReturn(PETSC_SUCCESS);
7063 }
7064 
7065 /*
7066   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
7067 
7068   Input Parameters:
7069 + section - The section for this data layout
7070 . islocal - Is the section (and thus indices being requested) local or global?
7071 . point   - The point contributing dofs with these indices
7072 . off     - The global offset of this point
7073 . loff    - The local offset of each field
7074 . setBC   - The flag determining whether to include indices of boundary values
7075 . perm    - A permutation of the dofs on this point, or NULL
7076 - indperm - A permutation of the entire indices array, or NULL
7077 
7078   Output Parameter:
7079 . indices - Indices for dofs on this point
7080 
7081   Level: developer
7082 
7083   Note: The indices could be local or global, depending on the value of 'off'.
7084 */
7085 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
7086 {
7087   PetscInt        dof;   /* The number of unknowns on this point */
7088   PetscInt        cdof;  /* The number of constraints on this point */
7089   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7090   PetscInt        cind = 0, k;
7091 
7092   PetscFunctionBegin;
7093   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7094   PetscCall(PetscSectionGetDof(section, point, &dof));
7095   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
7096   if (!cdof || setBC) {
7097     for (k = 0; k < dof; ++k) {
7098       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7099       const PetscInt ind    = indperm ? indperm[preind] : preind;
7100 
7101       indices[ind] = off + k;
7102     }
7103   } else {
7104     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
7105     for (k = 0; k < dof; ++k) {
7106       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7107       const PetscInt ind    = indperm ? indperm[preind] : preind;
7108 
7109       if ((cind < cdof) && (k == cdofs[cind])) {
7110         /* Insert check for returning constrained indices */
7111         indices[ind] = -(off + k + 1);
7112         ++cind;
7113       } else {
7114         indices[ind] = off + k - (islocal ? 0 : cind);
7115       }
7116     }
7117   }
7118   *loff += dof;
7119   PetscFunctionReturn(PETSC_SUCCESS);
7120 }
7121 
7122 /*
7123  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
7124 
7125  Input Parameters:
7126 + section - a section (global or local)
7127 - islocal - `PETSC_TRUE` if requesting local indices (i.e., section is local); `PETSC_FALSE` for global
7128 . point - point within section
7129 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
7130 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
7131 . setBC - identify constrained (boundary condition) points via involution.
7132 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
7133 . permsoff - offset
7134 - indperm - index permutation
7135 
7136  Output Parameter:
7137 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
7138 . indices - array to hold indices (as defined by section) of each dof associated with point
7139 
7140  Notes:
7141  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
7142  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
7143  in the local vector.
7144 
7145  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
7146  significant).  It is invalid to call with a global section and setBC=true.
7147 
7148  Developer Note:
7149  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
7150  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
7151  offset could be obtained from the section instead of passing it explicitly as we do now.
7152 
7153  Example:
7154  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
7155  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
7156  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
7157  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.
7158 
7159  Level: developer
7160 */
7161 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[])
7162 {
7163   PetscInt numFields, foff, f;
7164 
7165   PetscFunctionBegin;
7166   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7167   PetscCall(PetscSectionGetNumFields(section, &numFields));
7168   for (f = 0, foff = 0; f < numFields; ++f) {
7169     PetscInt        fdof, cfdof;
7170     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7171     PetscInt        cind = 0, b;
7172     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7173 
7174     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7175     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7176     if (!cfdof || setBC) {
7177       for (b = 0; b < fdof; ++b) {
7178         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7179         const PetscInt ind    = indperm ? indperm[preind] : preind;
7180 
7181         indices[ind] = off + foff + b;
7182       }
7183     } else {
7184       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7185       for (b = 0; b < fdof; ++b) {
7186         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7187         const PetscInt ind    = indperm ? indperm[preind] : preind;
7188 
7189         if ((cind < cfdof) && (b == fcdofs[cind])) {
7190           indices[ind] = -(off + foff + b + 1);
7191           ++cind;
7192         } else {
7193           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7194         }
7195       }
7196     }
7197     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7198     foffs[f] += fdof;
7199   }
7200   PetscFunctionReturn(PETSC_SUCCESS);
7201 }
7202 
7203 /*
7204   This version believes the globalSection offsets for each field, rather than just the point offset
7205 
7206  . foffs - The offset into 'indices' for each field, since it is segregated by field
7207 
7208  Notes:
7209  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7210  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7211 */
7212 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7213 {
7214   PetscInt numFields, foff, f;
7215 
7216   PetscFunctionBegin;
7217   PetscCall(PetscSectionGetNumFields(section, &numFields));
7218   for (f = 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     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7227     if (!cfdof) {
7228       for (b = 0; b < fdof; ++b) {
7229         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7230         const PetscInt ind    = indperm ? indperm[preind] : preind;
7231 
7232         indices[ind] = foff + b;
7233       }
7234     } else {
7235       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7236       for (b = 0; b < fdof; ++b) {
7237         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7238         const PetscInt ind    = indperm ? indperm[preind] : preind;
7239 
7240         if ((cind < cfdof) && (b == fcdofs[cind])) {
7241           indices[ind] = -(foff + b + 1);
7242           ++cind;
7243         } else {
7244           indices[ind] = foff + b - cind;
7245         }
7246       }
7247     }
7248     foffs[f] += fdof;
7249   }
7250   PetscFunctionReturn(PETSC_SUCCESS);
7251 }
7252 
7253 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)
7254 {
7255   Mat             cMat;
7256   PetscSection    aSec, cSec;
7257   IS              aIS;
7258   PetscInt        aStart = -1, aEnd = -1;
7259   const PetscInt *anchors;
7260   PetscInt        numFields, f, p, q, newP = 0;
7261   PetscInt        newNumPoints = 0, newNumIndices = 0;
7262   PetscInt       *newPoints, *indices, *newIndices;
7263   PetscInt        maxAnchor, maxDof;
7264   PetscInt        newOffsets[32];
7265   PetscInt       *pointMatOffsets[32];
7266   PetscInt       *newPointOffsets[32];
7267   PetscScalar    *pointMat[32];
7268   PetscScalar    *newValues      = NULL, *tmpValues;
7269   PetscBool       anyConstrained = PETSC_FALSE;
7270 
7271   PetscFunctionBegin;
7272   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7273   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7274   PetscCall(PetscSectionGetNumFields(section, &numFields));
7275 
7276   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7277   /* if there are point-to-point constraints */
7278   if (aSec) {
7279     PetscCall(PetscArrayzero(newOffsets, 32));
7280     PetscCall(ISGetIndices(aIS, &anchors));
7281     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7282     /* figure out how many points are going to be in the new element matrix
7283      * (we allow double counting, because it's all just going to be summed
7284      * into the global matrix anyway) */
7285     for (p = 0; p < 2 * numPoints; p += 2) {
7286       PetscInt b    = points[p];
7287       PetscInt bDof = 0, bSecDof;
7288 
7289       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7290       if (!bSecDof) continue;
7291       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7292       if (bDof) {
7293         /* this point is constrained */
7294         /* it is going to be replaced by its anchors */
7295         PetscInt bOff, q;
7296 
7297         anyConstrained = PETSC_TRUE;
7298         newNumPoints += bDof;
7299         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7300         for (q = 0; q < bDof; q++) {
7301           PetscInt a = anchors[bOff + q];
7302           PetscInt aDof;
7303 
7304           PetscCall(PetscSectionGetDof(section, a, &aDof));
7305           newNumIndices += aDof;
7306           for (f = 0; f < numFields; ++f) {
7307             PetscInt fDof;
7308 
7309             PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7310             newOffsets[f + 1] += fDof;
7311           }
7312         }
7313       } else {
7314         /* this point is not constrained */
7315         newNumPoints++;
7316         newNumIndices += bSecDof;
7317         for (f = 0; f < numFields; ++f) {
7318           PetscInt fDof;
7319 
7320           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7321           newOffsets[f + 1] += fDof;
7322         }
7323       }
7324     }
7325   }
7326   if (!anyConstrained) {
7327     if (outNumPoints) *outNumPoints = 0;
7328     if (outNumIndices) *outNumIndices = 0;
7329     if (outPoints) *outPoints = NULL;
7330     if (outValues) *outValues = NULL;
7331     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7332     PetscFunctionReturn(PETSC_SUCCESS);
7333   }
7334 
7335   if (outNumPoints) *outNumPoints = newNumPoints;
7336   if (outNumIndices) *outNumIndices = newNumIndices;
7337 
7338   for (f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7339 
7340   if (!outPoints && !outValues) {
7341     if (offsets) {
7342       for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7343     }
7344     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7345     PetscFunctionReturn(PETSC_SUCCESS);
7346   }
7347 
7348   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7349 
7350   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7351 
7352   /* workspaces */
7353   if (numFields) {
7354     for (f = 0; f < numFields; f++) {
7355       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7356       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7357     }
7358   } else {
7359     PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7360     PetscCall(DMGetWorkArray(dm, numPoints, MPIU_INT, &newPointOffsets[0]));
7361   }
7362 
7363   /* get workspaces for the point-to-point matrices */
7364   if (numFields) {
7365     PetscInt totalOffset, totalMatOffset;
7366 
7367     for (p = 0; p < numPoints; p++) {
7368       PetscInt b    = points[2 * p];
7369       PetscInt bDof = 0, bSecDof;
7370 
7371       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7372       if (!bSecDof) {
7373         for (f = 0; f < numFields; f++) {
7374           newPointOffsets[f][p + 1] = 0;
7375           pointMatOffsets[f][p + 1] = 0;
7376         }
7377         continue;
7378       }
7379       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7380       if (bDof) {
7381         for (f = 0; f < numFields; f++) {
7382           PetscInt fDof, q, bOff, allFDof = 0;
7383 
7384           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7385           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7386           for (q = 0; q < bDof; q++) {
7387             PetscInt a = anchors[bOff + q];
7388             PetscInt aFDof;
7389 
7390             PetscCall(PetscSectionGetFieldDof(section, a, f, &aFDof));
7391             allFDof += aFDof;
7392           }
7393           newPointOffsets[f][p + 1] = allFDof;
7394           pointMatOffsets[f][p + 1] = fDof * allFDof;
7395         }
7396       } else {
7397         for (f = 0; f < numFields; f++) {
7398           PetscInt fDof;
7399 
7400           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7401           newPointOffsets[f][p + 1] = fDof;
7402           pointMatOffsets[f][p + 1] = 0;
7403         }
7404       }
7405     }
7406     for (f = 0, totalOffset = 0, totalMatOffset = 0; f < numFields; f++) {
7407       newPointOffsets[f][0] = totalOffset;
7408       pointMatOffsets[f][0] = totalMatOffset;
7409       for (p = 0; p < numPoints; p++) {
7410         newPointOffsets[f][p + 1] += newPointOffsets[f][p];
7411         pointMatOffsets[f][p + 1] += pointMatOffsets[f][p];
7412       }
7413       totalOffset    = newPointOffsets[f][numPoints];
7414       totalMatOffset = pointMatOffsets[f][numPoints];
7415       PetscCall(DMGetWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7416     }
7417   } else {
7418     for (p = 0; p < numPoints; p++) {
7419       PetscInt b    = points[2 * p];
7420       PetscInt bDof = 0, bSecDof;
7421 
7422       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7423       if (!bSecDof) {
7424         newPointOffsets[0][p + 1] = 0;
7425         pointMatOffsets[0][p + 1] = 0;
7426         continue;
7427       }
7428       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7429       if (bDof) {
7430         PetscInt bOff, q, allDof = 0;
7431 
7432         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7433         for (q = 0; q < bDof; q++) {
7434           PetscInt a = anchors[bOff + q], aDof;
7435 
7436           PetscCall(PetscSectionGetDof(section, a, &aDof));
7437           allDof += aDof;
7438         }
7439         newPointOffsets[0][p + 1] = allDof;
7440         pointMatOffsets[0][p + 1] = bSecDof * allDof;
7441       } else {
7442         newPointOffsets[0][p + 1] = bSecDof;
7443         pointMatOffsets[0][p + 1] = 0;
7444       }
7445     }
7446     newPointOffsets[0][0] = 0;
7447     pointMatOffsets[0][0] = 0;
7448     for (p = 0; p < numPoints; p++) {
7449       newPointOffsets[0][p + 1] += newPointOffsets[0][p];
7450       pointMatOffsets[0][p + 1] += pointMatOffsets[0][p];
7451     }
7452     PetscCall(DMGetWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7453   }
7454 
7455   /* output arrays */
7456   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7457 
7458   /* get the point-to-point matrices; construct newPoints */
7459   PetscCall(PetscSectionGetMaxDof(aSec, &maxAnchor));
7460   PetscCall(PetscSectionGetMaxDof(section, &maxDof));
7461   PetscCall(DMGetWorkArray(dm, maxDof, MPIU_INT, &indices));
7462   PetscCall(DMGetWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7463   if (numFields) {
7464     for (p = 0, newP = 0; p < numPoints; p++) {
7465       PetscInt b    = points[2 * p];
7466       PetscInt o    = points[2 * p + 1];
7467       PetscInt bDof = 0, bSecDof;
7468 
7469       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7470       if (!bSecDof) continue;
7471       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7472       if (bDof) {
7473         PetscInt fStart[32], fEnd[32], fAnchorStart[32], fAnchorEnd[32], bOff, q;
7474 
7475         fStart[0] = 0;
7476         fEnd[0]   = 0;
7477         for (f = 0; f < numFields; f++) {
7478           PetscInt fDof;
7479 
7480           PetscCall(PetscSectionGetFieldDof(cSec, b, f, &fDof));
7481           fStart[f + 1] = fStart[f] + fDof;
7482           fEnd[f + 1]   = fStart[f + 1];
7483         }
7484         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7485         PetscCall(DMPlexGetIndicesPointFields_Internal(cSec, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, p, NULL, indices));
7486 
7487         fAnchorStart[0] = 0;
7488         fAnchorEnd[0]   = 0;
7489         for (f = 0; f < numFields; f++) {
7490           PetscInt fDof = newPointOffsets[f][p + 1] - newPointOffsets[f][p];
7491 
7492           fAnchorStart[f + 1] = fAnchorStart[f] + fDof;
7493           fAnchorEnd[f + 1]   = fAnchorStart[f + 1];
7494         }
7495         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7496         for (q = 0; q < bDof; q++) {
7497           PetscInt a = anchors[bOff + q], aOff;
7498 
7499           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7500           newPoints[2 * (newP + q)]     = a;
7501           newPoints[2 * (newP + q) + 1] = 0;
7502           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7503           PetscCall(DMPlexGetIndicesPointFields_Internal(section, PETSC_TRUE, a, aOff, fAnchorEnd, PETSC_TRUE, NULL, -1, NULL, newIndices));
7504         }
7505         newP += bDof;
7506 
7507         if (outValues) {
7508           /* get the point-to-point submatrix */
7509           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]));
7510         }
7511       } else {
7512         newPoints[2 * newP]     = b;
7513         newPoints[2 * newP + 1] = o;
7514         newP++;
7515       }
7516     }
7517   } else {
7518     for (p = 0; p < numPoints; p++) {
7519       PetscInt b    = points[2 * p];
7520       PetscInt o    = points[2 * p + 1];
7521       PetscInt bDof = 0, bSecDof;
7522 
7523       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7524       if (!bSecDof) continue;
7525       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7526       if (bDof) {
7527         PetscInt bEnd = 0, bAnchorEnd = 0, bOff;
7528 
7529         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7530         PetscCall(DMPlexGetIndicesPoint_Internal(cSec, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, indices));
7531 
7532         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7533         for (q = 0; q < bDof; q++) {
7534           PetscInt a = anchors[bOff + q], aOff;
7535 
7536           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7537 
7538           newPoints[2 * (newP + q)]     = a;
7539           newPoints[2 * (newP + q) + 1] = 0;
7540           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7541           PetscCall(DMPlexGetIndicesPoint_Internal(section, PETSC_TRUE, a, aOff, &bAnchorEnd, PETSC_TRUE, NULL, NULL, newIndices));
7542         }
7543         newP += bDof;
7544 
7545         /* get the point-to-point submatrix */
7546         if (outValues) PetscCall(MatGetValues(cMat, bEnd, indices, bAnchorEnd, newIndices, pointMat[0] + pointMatOffsets[0][p]));
7547       } else {
7548         newPoints[2 * newP]     = b;
7549         newPoints[2 * newP + 1] = o;
7550         newP++;
7551       }
7552     }
7553   }
7554 
7555   if (outValues) {
7556     PetscCall(DMGetWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7557     PetscCall(PetscArrayzero(tmpValues, newNumIndices * numIndices));
7558     /* multiply constraints on the right */
7559     if (numFields) {
7560       for (f = 0; f < numFields; f++) {
7561         PetscInt oldOff = offsets[f];
7562 
7563         for (p = 0; p < numPoints; p++) {
7564           PetscInt cStart = newPointOffsets[f][p];
7565           PetscInt b      = points[2 * p];
7566           PetscInt c, r, k;
7567           PetscInt dof;
7568 
7569           PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7570           if (!dof) continue;
7571           if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7572             PetscInt           nCols = newPointOffsets[f][p + 1] - cStart;
7573             const PetscScalar *mat   = pointMat[f] + pointMatOffsets[f][p];
7574 
7575             for (r = 0; r < numIndices; r++) {
7576               for (c = 0; c < nCols; c++) {
7577                 for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += values[r * numIndices + oldOff + k] * mat[k * nCols + c];
7578               }
7579             }
7580           } else {
7581             /* copy this column as is */
7582             for (r = 0; r < numIndices; r++) {
7583               for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7584             }
7585           }
7586           oldOff += dof;
7587         }
7588       }
7589     } else {
7590       PetscInt oldOff = 0;
7591       for (p = 0; p < numPoints; p++) {
7592         PetscInt cStart = newPointOffsets[0][p];
7593         PetscInt b      = points[2 * p];
7594         PetscInt c, r, k;
7595         PetscInt dof;
7596 
7597         PetscCall(PetscSectionGetDof(section, b, &dof));
7598         if (!dof) continue;
7599         if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7600           PetscInt           nCols = newPointOffsets[0][p + 1] - cStart;
7601           const PetscScalar *mat   = pointMat[0] + pointMatOffsets[0][p];
7602 
7603           for (r = 0; r < numIndices; r++) {
7604             for (c = 0; c < nCols; c++) {
7605               for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += mat[k * nCols + c] * values[r * numIndices + oldOff + k];
7606             }
7607           }
7608         } else {
7609           /* copy this column as is */
7610           for (r = 0; r < numIndices; r++) {
7611             for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7612           }
7613         }
7614         oldOff += dof;
7615       }
7616     }
7617 
7618     if (multiplyLeft) {
7619       PetscCall(DMGetWorkArray(dm, newNumIndices * newNumIndices, MPIU_SCALAR, &newValues));
7620       PetscCall(PetscArrayzero(newValues, newNumIndices * newNumIndices));
7621       /* multiply constraints transpose on the left */
7622       if (numFields) {
7623         for (f = 0; f < numFields; f++) {
7624           PetscInt oldOff = offsets[f];
7625 
7626           for (p = 0; p < numPoints; p++) {
7627             PetscInt rStart = newPointOffsets[f][p];
7628             PetscInt b      = points[2 * p];
7629             PetscInt c, r, k;
7630             PetscInt dof;
7631 
7632             PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7633             if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7634               PetscInt                          nRows = newPointOffsets[f][p + 1] - rStart;
7635               const PetscScalar *PETSC_RESTRICT mat   = pointMat[f] + pointMatOffsets[f][p];
7636 
7637               for (r = 0; r < nRows; r++) {
7638                 for (c = 0; c < newNumIndices; c++) {
7639                   for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7640                 }
7641               }
7642             } else {
7643               /* copy this row as is */
7644               for (r = 0; r < dof; r++) {
7645                 for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7646               }
7647             }
7648             oldOff += dof;
7649           }
7650         }
7651       } else {
7652         PetscInt oldOff = 0;
7653 
7654         for (p = 0; p < numPoints; p++) {
7655           PetscInt rStart = newPointOffsets[0][p];
7656           PetscInt b      = points[2 * p];
7657           PetscInt c, r, k;
7658           PetscInt dof;
7659 
7660           PetscCall(PetscSectionGetDof(section, b, &dof));
7661           if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7662             PetscInt                          nRows = newPointOffsets[0][p + 1] - rStart;
7663             const PetscScalar *PETSC_RESTRICT mat   = pointMat[0] + pointMatOffsets[0][p];
7664 
7665             for (r = 0; r < nRows; r++) {
7666               for (c = 0; c < newNumIndices; c++) {
7667                 for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7668               }
7669             }
7670           } else {
7671             /* copy this row as is */
7672             for (r = 0; r < dof; r++) {
7673               for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7674             }
7675           }
7676           oldOff += dof;
7677         }
7678       }
7679 
7680       PetscCall(DMRestoreWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7681     } else {
7682       newValues = tmpValues;
7683     }
7684   }
7685 
7686   /* clean up */
7687   PetscCall(DMRestoreWorkArray(dm, maxDof, MPIU_INT, &indices));
7688   PetscCall(DMRestoreWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7689 
7690   if (numFields) {
7691     for (f = 0; f < numFields; f++) {
7692       PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7693       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7694       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7695     }
7696   } else {
7697     PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7698     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7699     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[0]));
7700   }
7701   PetscCall(ISRestoreIndices(aIS, &anchors));
7702 
7703   /* output */
7704   if (outPoints) {
7705     *outPoints = newPoints;
7706   } else {
7707     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7708   }
7709   if (outValues) *outValues = newValues;
7710   for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7711   PetscFunctionReturn(PETSC_SUCCESS);
7712 }
7713 
7714 /*@C
7715   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
7716 
7717   Not collective
7718 
7719   Input Parameters:
7720 + dm         - The `DM`
7721 . section    - The `PetscSection` describing the points (a local section)
7722 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
7723 . point      - The point defining the closure
7724 - useClPerm  - Use the closure point permutation if available
7725 
7726   Output Parameters:
7727 + numIndices - The number of dof indices in the closure of point with the input sections
7728 . indices    - The dof indices
7729 . outOffsets - Array to write the field offsets into, or `NULL`
7730 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
7731 
7732   Level: advanced
7733 
7734   Notes:
7735   Must call `DMPlexRestoreClosureIndices()` to free allocated memory
7736 
7737   If `idxSection` is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
7738   of those indices is not significant.  If `idxSection` is local, the constrained dofs will yield the involution -(idx+1)
7739   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7740   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when `idxSection` == section, otherwise global
7741   indices (with the above semantics) are implied.
7742 
7743 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
7744           `PetscSection`, `DMGetGlobalSection()`
7745 @*/
7746 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7747 {
7748   /* Closure ordering */
7749   PetscSection    clSection;
7750   IS              clPoints;
7751   const PetscInt *clp;
7752   PetscInt       *points;
7753   const PetscInt *clperm = NULL;
7754   /* Dof permutation and sign flips */
7755   const PetscInt    **perms[32] = {NULL};
7756   const PetscScalar **flips[32] = {NULL};
7757   PetscScalar        *valCopy   = NULL;
7758   /* Hanging node constraints */
7759   PetscInt    *pointsC = NULL;
7760   PetscScalar *valuesC = NULL;
7761   PetscInt     NclC, NiC;
7762 
7763   PetscInt *idx;
7764   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7765   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7766 
7767   PetscFunctionBeginHot;
7768   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7769   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7770   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7771   if (numIndices) PetscAssertPointer(numIndices, 6);
7772   if (indices) PetscAssertPointer(indices, 7);
7773   if (outOffsets) PetscAssertPointer(outOffsets, 8);
7774   if (values) PetscAssertPointer(values, 9);
7775   PetscCall(PetscSectionGetNumFields(section, &Nf));
7776   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7777   PetscCall(PetscArrayzero(offsets, 32));
7778   /* 1) Get points in closure */
7779   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7780   if (useClPerm) {
7781     PetscInt depth, clsize;
7782     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7783     for (clsize = 0, p = 0; p < Ncl; p++) {
7784       PetscInt dof;
7785       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7786       clsize += dof;
7787     }
7788     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7789   }
7790   /* 2) Get number of indices on these points and field offsets from section */
7791   for (p = 0; p < Ncl * 2; p += 2) {
7792     PetscInt dof, fdof;
7793 
7794     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7795     for (f = 0; f < Nf; ++f) {
7796       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7797       offsets[f + 1] += fdof;
7798     }
7799     Ni += dof;
7800   }
7801   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
7802   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7803   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7804   for (f = 0; f < PetscMax(1, Nf); ++f) {
7805     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7806     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
7807     /* may need to apply sign changes to the element matrix */
7808     if (values && flips[f]) {
7809       PetscInt foffset = offsets[f];
7810 
7811       for (p = 0; p < Ncl; ++p) {
7812         PetscInt           pnt  = points[2 * p], fdof;
7813         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
7814 
7815         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
7816         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
7817         if (flip) {
7818           PetscInt i, j, k;
7819 
7820           if (!valCopy) {
7821             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7822             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
7823             *values = valCopy;
7824           }
7825           for (i = 0; i < fdof; ++i) {
7826             PetscScalar fval = flip[i];
7827 
7828             for (k = 0; k < Ni; ++k) {
7829               valCopy[Ni * (foffset + i) + k] *= fval;
7830               valCopy[Ni * k + (foffset + i)] *= fval;
7831             }
7832           }
7833         }
7834         foffset += fdof;
7835       }
7836     }
7837   }
7838   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
7839   PetscCall(DMPlexAnchorsModifyMat(dm, section, Ncl, Ni, points, perms, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, PETSC_TRUE));
7840   if (NclC) {
7841     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7842     for (f = 0; f < PetscMax(1, Nf); ++f) {
7843       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7844       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7845     }
7846     for (f = 0; f < PetscMax(1, Nf); ++f) {
7847       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
7848       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
7849     }
7850     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7851     Ncl    = NclC;
7852     Ni     = NiC;
7853     points = pointsC;
7854     if (values) *values = valuesC;
7855   }
7856   /* 5) Calculate indices */
7857   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
7858   if (Nf) {
7859     PetscInt  idxOff;
7860     PetscBool useFieldOffsets;
7861 
7862     if (outOffsets) {
7863       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
7864     }
7865     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
7866     if (useFieldOffsets) {
7867       for (p = 0; p < Ncl; ++p) {
7868         const PetscInt pnt = points[p * 2];
7869 
7870         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
7871       }
7872     } else {
7873       for (p = 0; p < Ncl; ++p) {
7874         const PetscInt pnt = points[p * 2];
7875 
7876         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7877         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7878          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
7879          * global section. */
7880         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
7881       }
7882     }
7883   } else {
7884     PetscInt off = 0, idxOff;
7885 
7886     for (p = 0; p < Ncl; ++p) {
7887       const PetscInt  pnt  = points[p * 2];
7888       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
7889 
7890       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7891       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7892        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
7893       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
7894     }
7895   }
7896   /* 6) Cleanup */
7897   for (f = 0; f < PetscMax(1, Nf); ++f) {
7898     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7899     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7900   }
7901   if (NclC) {
7902     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
7903   } else {
7904     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7905   }
7906 
7907   if (numIndices) *numIndices = Ni;
7908   if (indices) *indices = idx;
7909   PetscFunctionReturn(PETSC_SUCCESS);
7910 }
7911 
7912 /*@C
7913   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
7914 
7915   Not collective
7916 
7917   Input Parameters:
7918 + dm         - The `DM`
7919 . section    - The `PetscSection` describing the points (a local section)
7920 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
7921 . point      - The point defining the closure
7922 - useClPerm  - Use the closure point permutation if available
7923 
7924   Output Parameters:
7925 + numIndices - The number of dof indices in the closure of point with the input sections
7926 . indices    - The dof indices
7927 . outOffsets - Array to write the field offsets into, or `NULL`
7928 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
7929 
7930   Level: advanced
7931 
7932   Notes:
7933   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
7934 
7935   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
7936   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7937   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7938   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7939   indices (with the above semantics) are implied.
7940 
7941 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7942 @*/
7943 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7944 {
7945   PetscFunctionBegin;
7946   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7947   PetscAssertPointer(indices, 7);
7948   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
7949   PetscFunctionReturn(PETSC_SUCCESS);
7950 }
7951 
7952 PetscErrorCode DMPlexMatSetClosure_Internal(DM dm, PetscSection section, PetscSection globalSection, PetscBool useClPerm, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7953 {
7954   DM_Plex           *mesh = (DM_Plex *)dm->data;
7955   PetscInt          *indices;
7956   PetscInt           numIndices;
7957   const PetscScalar *valuesOrig = values;
7958   PetscErrorCode     ierr;
7959 
7960   PetscFunctionBegin;
7961   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7962   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7963   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7964   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
7965   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
7966   PetscValidHeaderSpecific(A, MAT_CLASSID, 5);
7967 
7968   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, useClPerm, &numIndices, &indices, NULL, (PetscScalar **)&values));
7969 
7970   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
7971   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7972   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
7973   if (ierr) {
7974     PetscMPIInt rank;
7975 
7976     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7977     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7978     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
7979     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7980     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7981     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
7982   }
7983   if (mesh->printFEM > 1) {
7984     PetscInt i;
7985     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
7986     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
7987     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
7988   }
7989 
7990   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7991   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7992   PetscFunctionReturn(PETSC_SUCCESS);
7993 }
7994 
7995 /*@C
7996   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
7997 
7998   Not collective
7999 
8000   Input Parameters:
8001 + dm            - The `DM`
8002 . section       - The section describing the layout in `v`, or `NULL` to use the default section
8003 . globalSection - The section describing the layout in `v`, or `NULL` to use the default global section
8004 . A             - The matrix
8005 . point         - The point in the `DM`
8006 . values        - The array of values
8007 - mode          - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8008 
8009   Level: intermediate
8010 
8011 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8012 @*/
8013 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8014 {
8015   PetscFunctionBegin;
8016   PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, PETSC_TRUE, A, point, values, mode));
8017   PetscFunctionReturn(PETSC_SUCCESS);
8018 }
8019 
8020 /*@C
8021   DMPlexMatSetClosureGeneral - Set an array of the values on the closure of 'point' using a different row and column section
8022 
8023   Not collective
8024 
8025   Input Parameters:
8026 + dmRow            - The `DM` for the row fields
8027 . sectionRow       - The section describing the layout, or `NULL` to use the default section in `dmRow`
8028 . useRowPerm       - The flag to use the closure permutation of the `dmRow` if available
8029 . globalSectionRow - The section describing the layout, or `NULL` to use the default global section in `dmRow`
8030 . dmCol            - The `DM` for the column fields
8031 . sectionCol       - The section describing the layout, or `NULL` to use the default section in `dmCol`
8032 . useColPerm       - The flag to use the closure permutation of the `dmCol` if available
8033 . globalSectionCol - The section describing the layout, or `NULL` to use the default global section in `dmCol`
8034 . A                - The matrix
8035 . point            - The point in the `DM`
8036 . values           - The array of values
8037 - mode             - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8038 
8039   Level: intermediate
8040 
8041 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8042 @*/
8043 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)
8044 {
8045   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
8046   PetscInt          *indicesRow, *indicesCol;
8047   PetscInt           numIndicesRow, numIndicesCol;
8048   const PetscScalar *valuesOrig = values;
8049   PetscErrorCode     ierr;
8050 
8051   PetscFunctionBegin;
8052   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
8053   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
8054   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
8055   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
8056   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
8057   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 5);
8058   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
8059   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 6);
8060   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
8061   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 7);
8062   PetscValidHeaderSpecific(A, MAT_CLASSID, 9);
8063 
8064   PetscCall(DMPlexGetClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
8065   PetscCall(DMPlexGetClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
8066 
8067   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
8068   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8069   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values, mode);
8070   if (ierr) {
8071     PetscMPIInt rank;
8072 
8073     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8074     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8075     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
8076     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
8077     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&values));
8078     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
8079   }
8080 
8081   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
8082   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
8083   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
8084   PetscFunctionReturn(PETSC_SUCCESS);
8085 }
8086 
8087 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8088 {
8089   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
8090   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8091   PetscInt       *cpoints = NULL;
8092   PetscInt       *findices, *cindices;
8093   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8094   PetscInt        foffsets[32], coffsets[32];
8095   DMPolytopeType  ct;
8096   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8097   PetscErrorCode  ierr;
8098 
8099   PetscFunctionBegin;
8100   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8101   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8102   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8103   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8104   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8105   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8106   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8107   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8108   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8109   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8110   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
8111   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8112   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8113   PetscCall(PetscArrayzero(foffsets, 32));
8114   PetscCall(PetscArrayzero(coffsets, 32));
8115   /* Column indices */
8116   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8117   maxFPoints = numCPoints;
8118   /* Compress out points not in the section */
8119   /*   TODO: Squeeze out points with 0 dof as well */
8120   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8121   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8122     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8123       cpoints[q * 2]     = cpoints[p];
8124       cpoints[q * 2 + 1] = cpoints[p + 1];
8125       ++q;
8126     }
8127   }
8128   numCPoints = q;
8129   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8130     PetscInt fdof;
8131 
8132     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8133     if (!dof) continue;
8134     for (f = 0; f < numFields; ++f) {
8135       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8136       coffsets[f + 1] += fdof;
8137     }
8138     numCIndices += dof;
8139   }
8140   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8141   /* Row indices */
8142   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8143   {
8144     DMPlexTransform tr;
8145     DMPolytopeType *rct;
8146     PetscInt       *rsize, *rcone, *rornt, Nt;
8147 
8148     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8149     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8150     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8151     numSubcells = rsize[Nt - 1];
8152     PetscCall(DMPlexTransformDestroy(&tr));
8153   }
8154   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8155   for (r = 0, q = 0; r < numSubcells; ++r) {
8156     /* TODO Map from coarse to fine cells */
8157     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8158     /* Compress out points not in the section */
8159     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8160     for (p = 0; p < numFPoints * 2; p += 2) {
8161       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8162         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8163         if (!dof) continue;
8164         for (s = 0; s < q; ++s)
8165           if (fpoints[p] == ftotpoints[s * 2]) break;
8166         if (s < q) continue;
8167         ftotpoints[q * 2]     = fpoints[p];
8168         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8169         ++q;
8170       }
8171     }
8172     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8173   }
8174   numFPoints = q;
8175   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8176     PetscInt fdof;
8177 
8178     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8179     if (!dof) continue;
8180     for (f = 0; f < numFields; ++f) {
8181       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8182       foffsets[f + 1] += fdof;
8183     }
8184     numFIndices += dof;
8185   }
8186   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8187 
8188   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8189   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8190   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8191   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8192   if (numFields) {
8193     const PetscInt **permsF[32] = {NULL};
8194     const PetscInt **permsC[32] = {NULL};
8195 
8196     for (f = 0; f < numFields; f++) {
8197       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8198       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8199     }
8200     for (p = 0; p < numFPoints; p++) {
8201       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8202       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8203     }
8204     for (p = 0; p < numCPoints; p++) {
8205       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8206       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8207     }
8208     for (f = 0; f < numFields; f++) {
8209       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8210       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8211     }
8212   } else {
8213     const PetscInt **permsF = NULL;
8214     const PetscInt **permsC = NULL;
8215 
8216     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8217     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8218     for (p = 0, off = 0; p < numFPoints; p++) {
8219       const PetscInt *perm = permsF ? permsF[p] : NULL;
8220 
8221       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8222       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8223     }
8224     for (p = 0, off = 0; p < numCPoints; p++) {
8225       const PetscInt *perm = permsC ? permsC[p] : NULL;
8226 
8227       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8228       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8229     }
8230     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8231     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8232   }
8233   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8234   /* TODO: flips */
8235   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8236   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8237   if (ierr) {
8238     PetscMPIInt rank;
8239 
8240     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8241     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8242     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8243     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8244     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8245   }
8246   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8247   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8248   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8249   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8250   PetscFunctionReturn(PETSC_SUCCESS);
8251 }
8252 
8253 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8254 {
8255   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8256   PetscInt       *cpoints = NULL;
8257   PetscInt        foffsets[32], coffsets[32];
8258   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8259   DMPolytopeType  ct;
8260   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8261 
8262   PetscFunctionBegin;
8263   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8264   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8265   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8266   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8267   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8268   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8269   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8270   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8271   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8272   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8273   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8274   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8275   PetscCall(PetscArrayzero(foffsets, 32));
8276   PetscCall(PetscArrayzero(coffsets, 32));
8277   /* Column indices */
8278   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8279   maxFPoints = numCPoints;
8280   /* Compress out points not in the section */
8281   /*   TODO: Squeeze out points with 0 dof as well */
8282   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8283   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8284     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8285       cpoints[q * 2]     = cpoints[p];
8286       cpoints[q * 2 + 1] = cpoints[p + 1];
8287       ++q;
8288     }
8289   }
8290   numCPoints = q;
8291   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8292     PetscInt fdof;
8293 
8294     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8295     if (!dof) continue;
8296     for (f = 0; f < numFields; ++f) {
8297       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8298       coffsets[f + 1] += fdof;
8299     }
8300     numCIndices += dof;
8301   }
8302   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8303   /* Row indices */
8304   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8305   {
8306     DMPlexTransform tr;
8307     DMPolytopeType *rct;
8308     PetscInt       *rsize, *rcone, *rornt, Nt;
8309 
8310     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8311     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8312     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8313     numSubcells = rsize[Nt - 1];
8314     PetscCall(DMPlexTransformDestroy(&tr));
8315   }
8316   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8317   for (r = 0, q = 0; r < numSubcells; ++r) {
8318     /* TODO Map from coarse to fine cells */
8319     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8320     /* Compress out points not in the section */
8321     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8322     for (p = 0; p < numFPoints * 2; p += 2) {
8323       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8324         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8325         if (!dof) continue;
8326         for (s = 0; s < q; ++s)
8327           if (fpoints[p] == ftotpoints[s * 2]) break;
8328         if (s < q) continue;
8329         ftotpoints[q * 2]     = fpoints[p];
8330         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8331         ++q;
8332       }
8333     }
8334     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8335   }
8336   numFPoints = q;
8337   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8338     PetscInt fdof;
8339 
8340     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8341     if (!dof) continue;
8342     for (f = 0; f < numFields; ++f) {
8343       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8344       foffsets[f + 1] += fdof;
8345     }
8346     numFIndices += dof;
8347   }
8348   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8349 
8350   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8351   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8352   if (numFields) {
8353     const PetscInt **permsF[32] = {NULL};
8354     const PetscInt **permsC[32] = {NULL};
8355 
8356     for (f = 0; f < numFields; f++) {
8357       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8358       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8359     }
8360     for (p = 0; p < numFPoints; p++) {
8361       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8362       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8363     }
8364     for (p = 0; p < numCPoints; p++) {
8365       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8366       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8367     }
8368     for (f = 0; f < numFields; f++) {
8369       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8370       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8371     }
8372   } else {
8373     const PetscInt **permsF = NULL;
8374     const PetscInt **permsC = NULL;
8375 
8376     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8377     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8378     for (p = 0, off = 0; p < numFPoints; p++) {
8379       const PetscInt *perm = permsF ? permsF[p] : NULL;
8380 
8381       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8382       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8383     }
8384     for (p = 0, off = 0; p < numCPoints; p++) {
8385       const PetscInt *perm = permsC ? permsC[p] : NULL;
8386 
8387       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8388       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8389     }
8390     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8391     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8392   }
8393   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8394   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8395   PetscFunctionReturn(PETSC_SUCCESS);
8396 }
8397 
8398 /*@C
8399   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8400 
8401   Input Parameter:
8402 . dm - The `DMPLEX` object
8403 
8404   Output Parameter:
8405 . cellHeight - The height of a cell
8406 
8407   Level: developer
8408 
8409 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetVTKCellHeight()`
8410 @*/
8411 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8412 {
8413   DM_Plex *mesh = (DM_Plex *)dm->data;
8414 
8415   PetscFunctionBegin;
8416   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8417   PetscAssertPointer(cellHeight, 2);
8418   *cellHeight = mesh->vtkCellHeight;
8419   PetscFunctionReturn(PETSC_SUCCESS);
8420 }
8421 
8422 /*@C
8423   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8424 
8425   Input Parameters:
8426 + dm         - The `DMPLEX` object
8427 - cellHeight - The height of a cell
8428 
8429   Level: developer
8430 
8431 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8432 @*/
8433 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8434 {
8435   DM_Plex *mesh = (DM_Plex *)dm->data;
8436 
8437   PetscFunctionBegin;
8438   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8439   mesh->vtkCellHeight = cellHeight;
8440   PetscFunctionReturn(PETSC_SUCCESS);
8441 }
8442 
8443 /*@
8444   DMPlexGetCellTypeStratum - Get the range of cells of a given celltype
8445 
8446   Input Parameters:
8447 + dm - The `DMPLEX` object
8448 - ct - The `DMPolytopeType` of the cell
8449 
8450   Output Parameters:
8451 + start - The first cell of this type, or `NULL`
8452 - end   - The upper bound on this celltype, or `NULL`
8453 
8454   Level: advanced
8455 
8456 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8457 @*/
8458 PetscErrorCode DMPlexGetCellTypeStratum(DM dm, DMPolytopeType ct, PetscInt *start, PetscInt *end)
8459 {
8460   DM_Plex *mesh = (DM_Plex *)dm->data;
8461   DMLabel  label;
8462   PetscInt pStart, pEnd;
8463 
8464   PetscFunctionBegin;
8465   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8466   if (start) {
8467     PetscAssertPointer(start, 3);
8468     *start = 0;
8469   }
8470   if (end) {
8471     PetscAssertPointer(end, 4);
8472     *end = 0;
8473   }
8474   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8475   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
8476   if (mesh->tr) {
8477     PetscCall(DMPlexTransformGetCellTypeStratum(mesh->tr, ct, start, end));
8478   } else {
8479     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
8480     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named celltype was found");
8481     PetscCall(DMLabelGetStratumBounds(label, ct, start, end));
8482   }
8483   PetscFunctionReturn(PETSC_SUCCESS);
8484 }
8485 
8486 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8487 {
8488   PetscSection section, globalSection;
8489   PetscInt    *numbers, p;
8490 
8491   PetscFunctionBegin;
8492   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8493   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8494   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8495   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8496   PetscCall(PetscSectionSetUp(section));
8497   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_FALSE, &globalSection));
8498   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8499   for (p = pStart; p < pEnd; ++p) {
8500     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8501     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8502     else numbers[p - pStart] += shift;
8503   }
8504   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8505   if (globalSize) {
8506     PetscLayout layout;
8507     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8508     PetscCall(PetscLayoutGetSize(layout, globalSize));
8509     PetscCall(PetscLayoutDestroy(&layout));
8510   }
8511   PetscCall(PetscSectionDestroy(&section));
8512   PetscCall(PetscSectionDestroy(&globalSection));
8513   PetscFunctionReturn(PETSC_SUCCESS);
8514 }
8515 
8516 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
8517 {
8518   PetscInt cellHeight, cStart, cEnd;
8519 
8520   PetscFunctionBegin;
8521   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8522   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8523   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8524   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8525   PetscFunctionReturn(PETSC_SUCCESS);
8526 }
8527 
8528 /*@
8529   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8530 
8531   Input Parameter:
8532 . dm - The `DMPLEX` object
8533 
8534   Output Parameter:
8535 . globalCellNumbers - Global cell numbers for all cells on this process
8536 
8537   Level: developer
8538 
8539 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVertexNumbering()`
8540 @*/
8541 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8542 {
8543   DM_Plex *mesh = (DM_Plex *)dm->data;
8544 
8545   PetscFunctionBegin;
8546   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8547   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8548   *globalCellNumbers = mesh->globalCellNumbers;
8549   PetscFunctionReturn(PETSC_SUCCESS);
8550 }
8551 
8552 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8553 {
8554   PetscInt vStart, vEnd;
8555 
8556   PetscFunctionBegin;
8557   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8558   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8559   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8560   PetscFunctionReturn(PETSC_SUCCESS);
8561 }
8562 
8563 /*@
8564   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8565 
8566   Input Parameter:
8567 . dm - The `DMPLEX` object
8568 
8569   Output Parameter:
8570 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8571 
8572   Level: developer
8573 
8574 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8575 @*/
8576 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8577 {
8578   DM_Plex *mesh = (DM_Plex *)dm->data;
8579 
8580   PetscFunctionBegin;
8581   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8582   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8583   *globalVertexNumbers = mesh->globalVertexNumbers;
8584   PetscFunctionReturn(PETSC_SUCCESS);
8585 }
8586 
8587 /*@
8588   DMPlexCreatePointNumbering - Create a global numbering for all points.
8589 
8590   Collective
8591 
8592   Input Parameter:
8593 . dm - The `DMPLEX` object
8594 
8595   Output Parameter:
8596 . globalPointNumbers - Global numbers for all points on this process
8597 
8598   Level: developer
8599 
8600   Notes:
8601   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8602   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8603   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8604   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8605 
8606   The partitioned mesh is
8607   ```
8608   (2)--0--(3)--1--(4)    (1)--0--(2)
8609   ```
8610   and its global numbering is
8611   ```
8612   (3)--0--(4)--1--(5)--2--(6)
8613   ```
8614   Then the global numbering is provided as
8615   ```
8616   [0] Number of indices in set 5
8617   [0] 0 0
8618   [0] 1 1
8619   [0] 2 3
8620   [0] 3 4
8621   [0] 4 -6
8622   [1] Number of indices in set 3
8623   [1] 0 2
8624   [1] 1 5
8625   [1] 2 6
8626   ```
8627 
8628 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8629 @*/
8630 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8631 {
8632   IS        nums[4];
8633   PetscInt  depths[4], gdepths[4], starts[4];
8634   PetscInt  depth, d, shift = 0;
8635   PetscBool empty = PETSC_FALSE;
8636 
8637   PetscFunctionBegin;
8638   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8639   PetscCall(DMPlexGetDepth(dm, &depth));
8640   // For unstratified meshes use dim instead of depth
8641   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8642   // If any stratum is empty, we must mark all empty
8643   for (d = 0; d <= depth; ++d) {
8644     PetscInt end;
8645 
8646     depths[d] = depth - d;
8647     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8648     if (!(starts[d] - end)) empty = PETSC_TRUE;
8649   }
8650   if (empty)
8651     for (d = 0; d <= depth; ++d) {
8652       depths[d] = -1;
8653       starts[d] = -1;
8654     }
8655   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8656   PetscCall(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8657   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]);
8658   // Note here that 'shift' is collective, so that the numbering is stratified by depth
8659   for (d = 0; d <= depth; ++d) {
8660     PetscInt pStart, pEnd, gsize;
8661 
8662     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8663     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8664     shift += gsize;
8665   }
8666   PetscCall(ISConcatenate(PETSC_COMM_SELF, depth + 1, nums, globalPointNumbers));
8667   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8668   PetscFunctionReturn(PETSC_SUCCESS);
8669 }
8670 
8671 /*@
8672   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8673 
8674   Input Parameter:
8675 . dm - The `DMPLEX` object
8676 
8677   Output Parameter:
8678 . ranks - The rank field
8679 
8680   Options Database Key:
8681 . -dm_partition_view - Adds the rank field into the `DM` output from `-dm_view` using the same viewer
8682 
8683   Level: intermediate
8684 
8685 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8686 @*/
8687 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8688 {
8689   DM             rdm;
8690   PetscFE        fe;
8691   PetscScalar   *r;
8692   PetscMPIInt    rank;
8693   DMPolytopeType ct;
8694   PetscInt       dim, cStart, cEnd, c;
8695   PetscBool      simplex;
8696 
8697   PetscFunctionBeginUser;
8698   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8699   PetscAssertPointer(ranks, 2);
8700   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
8701   PetscCall(DMClone(dm, &rdm));
8702   PetscCall(DMGetDimension(rdm, &dim));
8703   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8704   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8705   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
8706   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8707   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
8708   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8709   PetscCall(PetscFEDestroy(&fe));
8710   PetscCall(DMCreateDS(rdm));
8711   PetscCall(DMCreateGlobalVector(rdm, ranks));
8712   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
8713   PetscCall(VecGetArray(*ranks, &r));
8714   for (c = cStart; c < cEnd; ++c) {
8715     PetscScalar *lr;
8716 
8717     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8718     if (lr) *lr = rank;
8719   }
8720   PetscCall(VecRestoreArray(*ranks, &r));
8721   PetscCall(DMDestroy(&rdm));
8722   PetscFunctionReturn(PETSC_SUCCESS);
8723 }
8724 
8725 /*@
8726   DMPlexCreateLabelField - Create a cell field whose value is the label value for that cell
8727 
8728   Input Parameters:
8729 + dm    - The `DMPLEX`
8730 - label - The `DMLabel`
8731 
8732   Output Parameter:
8733 . val - The label value field
8734 
8735   Options Database Key:
8736 . -dm_label_view - Adds the label value field into the `DM` output from `-dm_view` using the same viewer
8737 
8738   Level: intermediate
8739 
8740 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8741 @*/
8742 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
8743 {
8744   DM           rdm;
8745   PetscFE      fe;
8746   PetscScalar *v;
8747   PetscInt     dim, cStart, cEnd, c;
8748 
8749   PetscFunctionBeginUser;
8750   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8751   PetscAssertPointer(label, 2);
8752   PetscAssertPointer(val, 3);
8753   PetscCall(DMClone(dm, &rdm));
8754   PetscCall(DMGetDimension(rdm, &dim));
8755   PetscCall(PetscFECreateDefault(PetscObjectComm((PetscObject)rdm), dim, 1, PETSC_TRUE, "PETSc___label_value_", -1, &fe));
8756   PetscCall(PetscObjectSetName((PetscObject)fe, "label_value"));
8757   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8758   PetscCall(PetscFEDestroy(&fe));
8759   PetscCall(DMCreateDS(rdm));
8760   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8761   PetscCall(DMCreateGlobalVector(rdm, val));
8762   PetscCall(PetscObjectSetName((PetscObject)*val, "label_value"));
8763   PetscCall(VecGetArray(*val, &v));
8764   for (c = cStart; c < cEnd; ++c) {
8765     PetscScalar *lv;
8766     PetscInt     cval;
8767 
8768     PetscCall(DMPlexPointGlobalRef(rdm, c, v, &lv));
8769     PetscCall(DMLabelGetValue(label, c, &cval));
8770     *lv = cval;
8771   }
8772   PetscCall(VecRestoreArray(*val, &v));
8773   PetscCall(DMDestroy(&rdm));
8774   PetscFunctionReturn(PETSC_SUCCESS);
8775 }
8776 
8777 /*@
8778   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
8779 
8780   Input Parameter:
8781 . dm - The `DMPLEX` object
8782 
8783   Level: developer
8784 
8785   Notes:
8786   This is a useful diagnostic when creating meshes programmatically.
8787 
8788   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8789 
8790 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8791 @*/
8792 PetscErrorCode DMPlexCheckSymmetry(DM dm)
8793 {
8794   PetscSection    coneSection, supportSection;
8795   const PetscInt *cone, *support;
8796   PetscInt        coneSize, c, supportSize, s;
8797   PetscInt        pStart, pEnd, p, pp, csize, ssize;
8798   PetscBool       storagecheck = PETSC_TRUE;
8799 
8800   PetscFunctionBegin;
8801   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8802   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
8803   PetscCall(DMPlexGetConeSection(dm, &coneSection));
8804   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
8805   /* Check that point p is found in the support of its cone points, and vice versa */
8806   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8807   for (p = pStart; p < pEnd; ++p) {
8808     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
8809     PetscCall(DMPlexGetCone(dm, p, &cone));
8810     for (c = 0; c < coneSize; ++c) {
8811       PetscBool dup = PETSC_FALSE;
8812       PetscInt  d;
8813       for (d = c - 1; d >= 0; --d) {
8814         if (cone[c] == cone[d]) {
8815           dup = PETSC_TRUE;
8816           break;
8817         }
8818       }
8819       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
8820       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
8821       for (s = 0; s < supportSize; ++s) {
8822         if (support[s] == p) break;
8823       }
8824       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
8825         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
8826         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
8827         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8828         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
8829         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
8830         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8831         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]);
8832         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
8833       }
8834     }
8835     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
8836     if (p != pp) {
8837       storagecheck = PETSC_FALSE;
8838       continue;
8839     }
8840     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
8841     PetscCall(DMPlexGetSupport(dm, p, &support));
8842     for (s = 0; s < supportSize; ++s) {
8843       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
8844       PetscCall(DMPlexGetCone(dm, support[s], &cone));
8845       for (c = 0; c < coneSize; ++c) {
8846         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
8847         if (cone[c] != pp) {
8848           c = 0;
8849           break;
8850         }
8851         if (cone[c] == p) break;
8852       }
8853       if (c >= coneSize) {
8854         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
8855         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
8856         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8857         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
8858         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
8859         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8860         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
8861       }
8862     }
8863   }
8864   if (storagecheck) {
8865     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
8866     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
8867     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
8868   }
8869   PetscFunctionReturn(PETSC_SUCCESS);
8870 }
8871 
8872 /*
8873   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.
8874 */
8875 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
8876 {
8877   DMPolytopeType  cct;
8878   PetscInt        ptpoints[4];
8879   const PetscInt *cone, *ccone, *ptcone;
8880   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
8881 
8882   PetscFunctionBegin;
8883   *unsplit = 0;
8884   switch (ct) {
8885   case DM_POLYTOPE_POINT_PRISM_TENSOR:
8886     ptpoints[npt++] = c;
8887     break;
8888   case DM_POLYTOPE_SEG_PRISM_TENSOR:
8889     PetscCall(DMPlexGetCone(dm, c, &cone));
8890     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8891     for (cp = 0; cp < coneSize; ++cp) {
8892       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
8893       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
8894     }
8895     break;
8896   case DM_POLYTOPE_TRI_PRISM_TENSOR:
8897   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8898     PetscCall(DMPlexGetCone(dm, c, &cone));
8899     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8900     for (cp = 0; cp < coneSize; ++cp) {
8901       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
8902       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
8903       for (ccp = 0; ccp < cconeSize; ++ccp) {
8904         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
8905         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
8906           PetscInt p;
8907           for (p = 0; p < npt; ++p)
8908             if (ptpoints[p] == ccone[ccp]) break;
8909           if (p == npt) ptpoints[npt++] = ccone[ccp];
8910         }
8911       }
8912     }
8913     break;
8914   default:
8915     break;
8916   }
8917   for (pt = 0; pt < npt; ++pt) {
8918     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
8919     if (ptcone[0] == ptcone[1]) ++(*unsplit);
8920   }
8921   PetscFunctionReturn(PETSC_SUCCESS);
8922 }
8923 
8924 /*@
8925   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
8926 
8927   Input Parameters:
8928 + dm         - The `DMPLEX` object
8929 - cellHeight - Normally 0
8930 
8931   Level: developer
8932 
8933   Notes:
8934   This is a useful diagnostic when creating meshes programmatically.
8935   Currently applicable only to homogeneous simplex or tensor meshes.
8936 
8937   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8938 
8939 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8940 @*/
8941 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
8942 {
8943   DMPlexInterpolatedFlag interp;
8944   DMPolytopeType         ct;
8945   PetscInt               vStart, vEnd, cStart, cEnd, c;
8946 
8947   PetscFunctionBegin;
8948   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8949   PetscCall(DMPlexIsInterpolated(dm, &interp));
8950   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8951   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8952   for (c = cStart; c < cEnd; ++c) {
8953     PetscInt *closure = NULL;
8954     PetscInt  coneSize, closureSize, cl, Nv = 0;
8955 
8956     PetscCall(DMPlexGetCellType(dm, c, &ct));
8957     PetscCheck((PetscInt)ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
8958     if (ct == DM_POLYTOPE_UNKNOWN) continue;
8959     if (interp == DMPLEX_INTERPOLATED_FULL) {
8960       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8961       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));
8962     }
8963     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8964     for (cl = 0; cl < closureSize * 2; cl += 2) {
8965       const PetscInt p = closure[cl];
8966       if ((p >= vStart) && (p < vEnd)) ++Nv;
8967     }
8968     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8969     /* Special Case: Tensor faces with identified vertices */
8970     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
8971       PetscInt unsplit;
8972 
8973       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8974       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
8975     }
8976     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));
8977   }
8978   PetscFunctionReturn(PETSC_SUCCESS);
8979 }
8980 
8981 /*@
8982   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
8983 
8984   Collective
8985 
8986   Input Parameters:
8987 + dm         - The `DMPLEX` object
8988 - cellHeight - Normally 0
8989 
8990   Level: developer
8991 
8992   Notes:
8993   This is a useful diagnostic when creating meshes programmatically.
8994   This routine is only relevant for meshes that are fully interpolated across all ranks.
8995   It will error out if a partially interpolated mesh is given on some rank.
8996   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
8997 
8998   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8999 
9000 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
9001 @*/
9002 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
9003 {
9004   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
9005   DMPlexInterpolatedFlag interpEnum;
9006 
9007   PetscFunctionBegin;
9008   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9009   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
9010   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(PETSC_SUCCESS);
9011   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
9012     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported"));
9013     PetscFunctionReturn(PETSC_SUCCESS);
9014   }
9015 
9016   PetscCall(DMGetDimension(dm, &dim));
9017   PetscCall(DMPlexGetDepth(dm, &depth));
9018   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9019   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
9020     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
9021     for (c = cStart; c < cEnd; ++c) {
9022       const PetscInt       *cone, *ornt, *faceSizes, *faces;
9023       const DMPolytopeType *faceTypes;
9024       DMPolytopeType        ct;
9025       PetscInt              numFaces, coneSize, f;
9026       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
9027 
9028       PetscCall(DMPlexGetCellType(dm, c, &ct));
9029       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9030       if (unsplit) continue;
9031       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9032       PetscCall(DMPlexGetCone(dm, c, &cone));
9033       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
9034       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9035       for (cl = 0; cl < closureSize * 2; cl += 2) {
9036         const PetscInt p = closure[cl];
9037         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
9038       }
9039       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9040       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);
9041       for (f = 0; f < numFaces; ++f) {
9042         DMPolytopeType fct;
9043         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
9044 
9045         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
9046         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
9047         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
9048           const PetscInt p = fclosure[cl];
9049           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
9050         }
9051         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]);
9052         for (v = 0; v < fnumCorners; ++v) {
9053           if (fclosure[v] != faces[fOff + v]) {
9054             PetscInt v1;
9055 
9056             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
9057             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
9058             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
9059             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
9060             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9061             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]);
9062           }
9063         }
9064         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
9065         fOff += faceSizes[f];
9066       }
9067       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9068       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9069     }
9070   }
9071   PetscFunctionReturn(PETSC_SUCCESS);
9072 }
9073 
9074 /*@
9075   DMPlexCheckGeometry - Check the geometry of mesh cells
9076 
9077   Input Parameter:
9078 . dm - The `DMPLEX` object
9079 
9080   Level: developer
9081 
9082   Notes:
9083   This is a useful diagnostic when creating meshes programmatically.
9084 
9085   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9086 
9087 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9088 @*/
9089 PetscErrorCode DMPlexCheckGeometry(DM dm)
9090 {
9091   Vec       coordinates;
9092   PetscReal detJ, J[9], refVol = 1.0;
9093   PetscReal vol;
9094   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
9095 
9096   PetscFunctionBegin;
9097   PetscCall(DMGetDimension(dm, &dim));
9098   PetscCall(DMGetCoordinateDim(dm, &dE));
9099   if (dim != dE) PetscFunctionReturn(PETSC_SUCCESS);
9100   PetscCall(DMPlexGetDepth(dm, &depth));
9101   for (d = 0; d < dim; ++d) refVol *= 2.0;
9102   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9103   /* Make sure local coordinates are created, because that step is collective */
9104   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
9105   if (!coordinates) PetscFunctionReturn(PETSC_SUCCESS);
9106   for (c = cStart; c < cEnd; ++c) {
9107     DMPolytopeType ct;
9108     PetscInt       unsplit;
9109     PetscBool      ignoreZeroVol = PETSC_FALSE;
9110 
9111     PetscCall(DMPlexGetCellType(dm, c, &ct));
9112     switch (ct) {
9113     case DM_POLYTOPE_SEG_PRISM_TENSOR:
9114     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9115     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9116       ignoreZeroVol = PETSC_TRUE;
9117       break;
9118     default:
9119       break;
9120     }
9121     switch (ct) {
9122     case DM_POLYTOPE_TRI_PRISM:
9123     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9124     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9125     case DM_POLYTOPE_PYRAMID:
9126       continue;
9127     default:
9128       break;
9129     }
9130     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9131     if (unsplit) continue;
9132     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
9133     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);
9134     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
9135     /* This should work with periodicity since DG coordinates should be used */
9136     if (depth > 1) {
9137       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
9138       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);
9139       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
9140     }
9141   }
9142   PetscFunctionReturn(PETSC_SUCCESS);
9143 }
9144 
9145 /*@
9146   DMPlexCheckPointSF - Check that several necessary conditions are met for the point `PetscSF` of this plex.
9147 
9148   Collective
9149 
9150   Input Parameters:
9151 + dm              - The `DMPLEX` object
9152 . pointSF         - The `PetscSF`, or `NULL` for `PointSF` attached to `DM`
9153 - allowExtraRoots - Flag to allow extra points not present in the `DM`
9154 
9155   Level: developer
9156 
9157   Notes:
9158   This is mainly intended for debugging/testing purposes.
9159 
9160   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9161 
9162   Extra roots can come from periodic cuts, where additional points appear on the boundary
9163 
9164 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
9165 @*/
9166 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
9167 {
9168   PetscInt           l, nleaves, nroots, overlap;
9169   const PetscInt    *locals;
9170   const PetscSFNode *remotes;
9171   PetscBool          distributed;
9172   MPI_Comm           comm;
9173   PetscMPIInt        rank;
9174 
9175   PetscFunctionBegin;
9176   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9177   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
9178   else pointSF = dm->sf;
9179   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9180   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
9181   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9182   {
9183     PetscMPIInt mpiFlag;
9184 
9185     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
9186     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
9187   }
9188   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
9189   PetscCall(DMPlexIsDistributed(dm, &distributed));
9190   if (!distributed) {
9191     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);
9192     PetscFunctionReturn(PETSC_SUCCESS);
9193   }
9194   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);
9195   PetscCall(DMPlexGetOverlap(dm, &overlap));
9196 
9197   /* Check SF graph is compatible with DMPlex chart */
9198   {
9199     PetscInt pStart, pEnd, maxLeaf;
9200 
9201     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9202     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9203     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9204     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9205   }
9206 
9207   /* Check Point SF has no local points referenced */
9208   for (l = 0; l < nleaves; l++) {
9209     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);
9210   }
9211 
9212   /* Check there are no cells in interface */
9213   if (!overlap) {
9214     PetscInt cellHeight, cStart, cEnd;
9215 
9216     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9217     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9218     for (l = 0; l < nleaves; ++l) {
9219       const PetscInt point = locals ? locals[l] : l;
9220 
9221       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9222     }
9223   }
9224 
9225   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9226   {
9227     const PetscInt *rootdegree;
9228 
9229     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9230     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9231     for (l = 0; l < nleaves; ++l) {
9232       const PetscInt  point = locals ? locals[l] : l;
9233       const PetscInt *cone;
9234       PetscInt        coneSize, c, idx;
9235 
9236       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9237       PetscCall(DMPlexGetCone(dm, point, &cone));
9238       for (c = 0; c < coneSize; ++c) {
9239         if (!rootdegree[cone[c]]) {
9240           if (locals) {
9241             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9242           } else {
9243             idx = (cone[c] < nleaves) ? cone[c] : -1;
9244           }
9245           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9246         }
9247       }
9248     }
9249   }
9250   PetscFunctionReturn(PETSC_SUCCESS);
9251 }
9252 
9253 /*@
9254   DMPlexCheck - Perform various checks of `DMPLEX` sanity
9255 
9256   Input Parameter:
9257 . dm - The `DMPLEX` object
9258 
9259   Level: developer
9260 
9261   Notes:
9262   This is a useful diagnostic when creating meshes programmatically.
9263 
9264   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9265 
9266   Currently does not include `DMPlexCheckCellShape()`.
9267 
9268 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9269 @*/
9270 PetscErrorCode DMPlexCheck(DM dm)
9271 {
9272   PetscInt cellHeight;
9273 
9274   PetscFunctionBegin;
9275   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9276   PetscCall(DMPlexCheckSymmetry(dm));
9277   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9278   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9279   PetscCall(DMPlexCheckGeometry(dm));
9280   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9281   PetscCall(DMPlexCheckInterfaceCones(dm));
9282   PetscFunctionReturn(PETSC_SUCCESS);
9283 }
9284 
9285 typedef struct cell_stats {
9286   PetscReal min, max, sum, squaresum;
9287   PetscInt  count;
9288 } cell_stats_t;
9289 
9290 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9291 {
9292   PetscInt i, N = *len;
9293 
9294   for (i = 0; i < N; i++) {
9295     cell_stats_t *A = (cell_stats_t *)a;
9296     cell_stats_t *B = (cell_stats_t *)b;
9297 
9298     B->min = PetscMin(A->min, B->min);
9299     B->max = PetscMax(A->max, B->max);
9300     B->sum += A->sum;
9301     B->squaresum += A->squaresum;
9302     B->count += A->count;
9303   }
9304 }
9305 
9306 /*@
9307   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9308 
9309   Collective
9310 
9311   Input Parameters:
9312 + dm        - The `DMPLEX` object
9313 . output    - If true, statistics will be displayed on `stdout`
9314 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9315 
9316   Level: developer
9317 
9318   Notes:
9319   This is mainly intended for debugging/testing purposes.
9320 
9321   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9322 
9323 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9324 @*/
9325 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9326 {
9327   DM           dmCoarse;
9328   cell_stats_t stats, globalStats;
9329   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9330   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9331   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9332   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9333   PetscMPIInt  rank, size;
9334 
9335   PetscFunctionBegin;
9336   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9337   stats.min = PETSC_MAX_REAL;
9338   stats.max = PETSC_MIN_REAL;
9339   stats.sum = stats.squaresum = 0.;
9340   stats.count                 = 0;
9341 
9342   PetscCallMPI(MPI_Comm_size(comm, &size));
9343   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9344   PetscCall(DMGetCoordinateDim(dm, &cdim));
9345   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9346   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9347   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9348   for (c = cStart; c < cEnd; c++) {
9349     PetscInt  i;
9350     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9351 
9352     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9353     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9354     for (i = 0; i < PetscSqr(cdim); ++i) {
9355       frobJ += J[i] * J[i];
9356       frobInvJ += invJ[i] * invJ[i];
9357     }
9358     cond2 = frobJ * frobInvJ;
9359     cond  = PetscSqrtReal(cond2);
9360 
9361     stats.min = PetscMin(stats.min, cond);
9362     stats.max = PetscMax(stats.max, cond);
9363     stats.sum += cond;
9364     stats.squaresum += cond2;
9365     stats.count++;
9366     if (output && cond > limit) {
9367       PetscSection coordSection;
9368       Vec          coordsLocal;
9369       PetscScalar *coords = NULL;
9370       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9371 
9372       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9373       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9374       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9375       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9376       for (i = 0; i < Nv / cdim; ++i) {
9377         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9378         for (d = 0; d < cdim; ++d) {
9379           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9380           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9381         }
9382         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9383       }
9384       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9385       for (cl = 0; cl < clSize * 2; cl += 2) {
9386         const PetscInt edge = closure[cl];
9387 
9388         if ((edge >= eStart) && (edge < eEnd)) {
9389           PetscReal len;
9390 
9391           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9392           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9393         }
9394       }
9395       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9396       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9397     }
9398   }
9399   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9400 
9401   if (size > 1) {
9402     PetscMPIInt  blockLengths[2] = {4, 1};
9403     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9404     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9405     MPI_Op       statReduce;
9406 
9407     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9408     PetscCallMPI(MPI_Type_commit(&statType));
9409     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9410     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9411     PetscCallMPI(MPI_Op_free(&statReduce));
9412     PetscCallMPI(MPI_Type_free(&statType));
9413   } else {
9414     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9415   }
9416   if (rank == 0) {
9417     count = globalStats.count;
9418     min   = globalStats.min;
9419     max   = globalStats.max;
9420     mean  = globalStats.sum / globalStats.count;
9421     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9422   }
9423 
9424   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));
9425   PetscCall(PetscFree2(J, invJ));
9426 
9427   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9428   if (dmCoarse) {
9429     PetscBool isplex;
9430 
9431     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9432     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9433   }
9434   PetscFunctionReturn(PETSC_SUCCESS);
9435 }
9436 
9437 /*@
9438   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9439   orthogonal quality below given tolerance.
9440 
9441   Collective
9442 
9443   Input Parameters:
9444 + dm   - The `DMPLEX` object
9445 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9446 - atol - [0, 1] Absolute tolerance for tagging cells.
9447 
9448   Output Parameters:
9449 + OrthQual      - `Vec` containing orthogonal quality per cell
9450 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9451 
9452   Options Database Keys:
9453 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9454 - -dm_plex_orthogonal_quality_vec_view   - view OrthQual vector.
9455 
9456   Level: intermediate
9457 
9458   Notes:
9459   Orthogonal quality is given by the following formula\:
9460 
9461   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9462 
9463   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
9464   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9465   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9466   calculating the cosine of the angle between these vectors.
9467 
9468   Orthogonal quality ranges from 1 (best) to 0 (worst).
9469 
9470   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9471   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9472 
9473   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9474 
9475 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9476 @*/
9477 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9478 {
9479   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9480   PetscInt              *idx;
9481   PetscScalar           *oqVals;
9482   const PetscScalar     *cellGeomArr, *faceGeomArr;
9483   PetscReal             *ci, *fi, *Ai;
9484   MPI_Comm               comm;
9485   Vec                    cellgeom, facegeom;
9486   DM                     dmFace, dmCell;
9487   IS                     glob;
9488   ISLocalToGlobalMapping ltog;
9489   PetscViewer            vwr;
9490 
9491   PetscFunctionBegin;
9492   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9493   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9494   PetscAssertPointer(OrthQual, 4);
9495   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9496   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9497   PetscCall(DMGetDimension(dm, &nc));
9498   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9499   {
9500     DMPlexInterpolatedFlag interpFlag;
9501 
9502     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9503     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9504       PetscMPIInt rank;
9505 
9506       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9507       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9508     }
9509   }
9510   if (OrthQualLabel) {
9511     PetscAssertPointer(OrthQualLabel, 5);
9512     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9513     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9514   } else {
9515     *OrthQualLabel = NULL;
9516   }
9517   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9518   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9519   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
9520   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9521   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9522   PetscCall(VecCreate(comm, OrthQual));
9523   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9524   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9525   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9526   PetscCall(VecSetUp(*OrthQual));
9527   PetscCall(ISDestroy(&glob));
9528   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9529   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9530   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9531   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9532   PetscCall(VecGetDM(cellgeom, &dmCell));
9533   PetscCall(VecGetDM(facegeom, &dmFace));
9534   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9535   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
9536     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9537     PetscInt         cellarr[2], *adj = NULL;
9538     PetscScalar     *cArr, *fArr;
9539     PetscReal        minvalc = 1.0, minvalf = 1.0;
9540     PetscFVCellGeom *cg;
9541 
9542     idx[cellIter] = cell - cStart;
9543     cellarr[0]    = cell;
9544     /* Make indexing into cellGeom easier */
9545     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9546     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9547     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9548     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9549     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
9550       PetscInt         i;
9551       const PetscInt   neigh  = adj[cellneigh];
9552       PetscReal        normci = 0, normfi = 0, normai = 0;
9553       PetscFVCellGeom *cgneigh;
9554       PetscFVFaceGeom *fg;
9555 
9556       /* Don't count ourselves in the neighbor list */
9557       if (neigh == cell) continue;
9558       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9559       cellarr[1] = neigh;
9560       {
9561         PetscInt        numcovpts;
9562         const PetscInt *covpts;
9563 
9564         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9565         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9566         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9567       }
9568 
9569       /* Compute c_i, f_i and their norms */
9570       for (i = 0; i < nc; i++) {
9571         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9572         fi[i] = fg->centroid[i] - cg->centroid[i];
9573         Ai[i] = fg->normal[i];
9574         normci += PetscPowReal(ci[i], 2);
9575         normfi += PetscPowReal(fi[i], 2);
9576         normai += PetscPowReal(Ai[i], 2);
9577       }
9578       normci = PetscSqrtReal(normci);
9579       normfi = PetscSqrtReal(normfi);
9580       normai = PetscSqrtReal(normai);
9581 
9582       /* Normalize and compute for each face-cell-normal pair */
9583       for (i = 0; i < nc; i++) {
9584         ci[i] = ci[i] / normci;
9585         fi[i] = fi[i] / normfi;
9586         Ai[i] = Ai[i] / normai;
9587         /* PetscAbs because I don't know if normals are guaranteed to point out */
9588         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
9589         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
9590       }
9591       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
9592       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
9593     }
9594     PetscCall(PetscFree(adj));
9595     PetscCall(PetscFree2(cArr, fArr));
9596     /* Defer to cell if they're equal */
9597     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9598     if (OrthQualLabel) {
9599       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9600     }
9601   }
9602   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
9603   PetscCall(VecAssemblyBegin(*OrthQual));
9604   PetscCall(VecAssemblyEnd(*OrthQual));
9605   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9606   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9607   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9608   if (OrthQualLabel) {
9609     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9610   }
9611   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9612   PetscCall(PetscViewerDestroy(&vwr));
9613   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9614   PetscFunctionReturn(PETSC_SUCCESS);
9615 }
9616 
9617 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9618  * interpolator construction */
9619 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9620 {
9621   PetscSection section, newSection, gsection;
9622   PetscSF      sf;
9623   PetscBool    hasConstraints, ghasConstraints;
9624 
9625   PetscFunctionBegin;
9626   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9627   PetscAssertPointer(odm, 2);
9628   PetscCall(DMGetLocalSection(dm, &section));
9629   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9630   PetscCall(MPIU_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
9631   if (!ghasConstraints) {
9632     PetscCall(PetscObjectReference((PetscObject)dm));
9633     *odm = dm;
9634     PetscFunctionReturn(PETSC_SUCCESS);
9635   }
9636   PetscCall(DMClone(dm, odm));
9637   PetscCall(DMCopyFields(dm, *odm));
9638   PetscCall(DMGetLocalSection(*odm, &newSection));
9639   PetscCall(DMGetPointSF(*odm, &sf));
9640   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_FALSE, &gsection));
9641   PetscCall(DMSetGlobalSection(*odm, gsection));
9642   PetscCall(PetscSectionDestroy(&gsection));
9643   PetscFunctionReturn(PETSC_SUCCESS);
9644 }
9645 
9646 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9647 {
9648   DM        dmco, dmfo;
9649   Mat       interpo;
9650   Vec       rscale;
9651   Vec       cglobalo, clocal;
9652   Vec       fglobal, fglobalo, flocal;
9653   PetscBool regular;
9654 
9655   PetscFunctionBegin;
9656   PetscCall(DMGetFullDM(dmc, &dmco));
9657   PetscCall(DMGetFullDM(dmf, &dmfo));
9658   PetscCall(DMSetCoarseDM(dmfo, dmco));
9659   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9660   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9661   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9662   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9663   PetscCall(DMCreateLocalVector(dmc, &clocal));
9664   PetscCall(VecSet(cglobalo, 0.));
9665   PetscCall(VecSet(clocal, 0.));
9666   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9667   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9668   PetscCall(DMCreateLocalVector(dmf, &flocal));
9669   PetscCall(VecSet(fglobal, 0.));
9670   PetscCall(VecSet(fglobalo, 0.));
9671   PetscCall(VecSet(flocal, 0.));
9672   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9673   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9674   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9675   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9676   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9677   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9678   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9679   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9680   *shift = fglobal;
9681   PetscCall(VecDestroy(&flocal));
9682   PetscCall(VecDestroy(&fglobalo));
9683   PetscCall(VecDestroy(&clocal));
9684   PetscCall(VecDestroy(&cglobalo));
9685   PetscCall(VecDestroy(&rscale));
9686   PetscCall(MatDestroy(&interpo));
9687   PetscCall(DMDestroy(&dmfo));
9688   PetscCall(DMDestroy(&dmco));
9689   PetscFunctionReturn(PETSC_SUCCESS);
9690 }
9691 
9692 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
9693 {
9694   PetscObject shifto;
9695   Vec         shift;
9696 
9697   PetscFunctionBegin;
9698   if (!interp) {
9699     Vec rscale;
9700 
9701     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9702     PetscCall(VecDestroy(&rscale));
9703   } else {
9704     PetscCall(PetscObjectReference((PetscObject)interp));
9705   }
9706   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9707   if (!shifto) {
9708     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9709     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
9710     shifto = (PetscObject)shift;
9711     PetscCall(VecDestroy(&shift));
9712   }
9713   shift = (Vec)shifto;
9714   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9715   PetscCall(VecAXPY(fineSol, 1.0, shift));
9716   PetscCall(MatDestroy(&interp));
9717   PetscFunctionReturn(PETSC_SUCCESS);
9718 }
9719 
9720 /* Pointwise interpolation
9721      Just code FEM for now
9722      u^f = I u^c
9723      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9724      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9725      I_{ij} = psi^f_i phi^c_j
9726 */
9727 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
9728 {
9729   PetscSection gsc, gsf;
9730   PetscInt     m, n;
9731   void        *ctx;
9732   DM           cdm;
9733   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9734 
9735   PetscFunctionBegin;
9736   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9737   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9738   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9739   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9740 
9741   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9742   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
9743   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9744   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9745   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9746 
9747   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9748   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9749   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9750   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9751   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9752   if (scaling) {
9753     /* Use naive scaling */
9754     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
9755   }
9756   PetscFunctionReturn(PETSC_SUCCESS);
9757 }
9758 
9759 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
9760 {
9761   VecScatter ctx;
9762 
9763   PetscFunctionBegin;
9764   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
9765   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
9766   PetscCall(VecScatterDestroy(&ctx));
9767   PetscFunctionReturn(PETSC_SUCCESS);
9768 }
9769 
9770 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[])
9771 {
9772   const PetscInt Nc = uOff[1] - uOff[0];
9773   PetscInt       c;
9774   for (c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
9775 }
9776 
9777 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
9778 {
9779   DM           dmc;
9780   PetscDS      ds;
9781   Vec          ones, locmass;
9782   IS           cellIS;
9783   PetscFormKey key;
9784   PetscInt     depth;
9785 
9786   PetscFunctionBegin;
9787   PetscCall(DMClone(dm, &dmc));
9788   PetscCall(DMCopyDisc(dm, dmc));
9789   PetscCall(DMGetDS(dmc, &ds));
9790   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9791   PetscCall(DMCreateGlobalVector(dmc, mass));
9792   PetscCall(DMGetLocalVector(dmc, &ones));
9793   PetscCall(DMGetLocalVector(dmc, &locmass));
9794   PetscCall(DMPlexGetDepth(dmc, &depth));
9795   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9796   PetscCall(VecSet(locmass, 0.0));
9797   PetscCall(VecSet(ones, 1.0));
9798   key.label = NULL;
9799   key.value = 0;
9800   key.field = 0;
9801   key.part  = 0;
9802   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
9803   PetscCall(ISDestroy(&cellIS));
9804   PetscCall(VecSet(*mass, 0.0));
9805   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
9806   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
9807   PetscCall(DMRestoreLocalVector(dmc, &ones));
9808   PetscCall(DMRestoreLocalVector(dmc, &locmass));
9809   PetscCall(DMDestroy(&dmc));
9810   PetscFunctionReturn(PETSC_SUCCESS);
9811 }
9812 
9813 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
9814 {
9815   PetscSection gsc, gsf;
9816   PetscInt     m, n;
9817   void        *ctx;
9818   DM           cdm;
9819   PetscBool    regular;
9820 
9821   PetscFunctionBegin;
9822   if (dmFine == dmCoarse) {
9823     DM            dmc;
9824     PetscDS       ds;
9825     PetscWeakForm wf;
9826     Vec           u;
9827     IS            cellIS;
9828     PetscFormKey  key;
9829     PetscInt      depth;
9830 
9831     PetscCall(DMClone(dmFine, &dmc));
9832     PetscCall(DMCopyDisc(dmFine, dmc));
9833     PetscCall(DMGetDS(dmc, &ds));
9834     PetscCall(PetscDSGetWeakForm(ds, &wf));
9835     PetscCall(PetscWeakFormClear(wf));
9836     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9837     PetscCall(DMCreateMatrix(dmc, mass));
9838     PetscCall(DMGetLocalVector(dmc, &u));
9839     PetscCall(DMPlexGetDepth(dmc, &depth));
9840     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9841     PetscCall(MatZeroEntries(*mass));
9842     key.label = NULL;
9843     key.value = 0;
9844     key.field = 0;
9845     key.part  = 0;
9846     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
9847     PetscCall(ISDestroy(&cellIS));
9848     PetscCall(DMRestoreLocalVector(dmc, &u));
9849     PetscCall(DMDestroy(&dmc));
9850   } else {
9851     PetscCall(DMGetGlobalSection(dmFine, &gsf));
9852     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9853     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9854     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9855 
9856     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
9857     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9858     PetscCall(MatSetType(*mass, dmCoarse->mattype));
9859     PetscCall(DMGetApplicationContext(dmFine, &ctx));
9860 
9861     PetscCall(DMGetCoarseDM(dmFine, &cdm));
9862     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9863     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
9864     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
9865   }
9866   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
9867   PetscFunctionReturn(PETSC_SUCCESS);
9868 }
9869 
9870 /*@
9871   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9872 
9873   Input Parameter:
9874 . dm - The `DMPLEX` object
9875 
9876   Output Parameter:
9877 . regular - The flag
9878 
9879   Level: intermediate
9880 
9881 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
9882 @*/
9883 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
9884 {
9885   PetscFunctionBegin;
9886   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9887   PetscAssertPointer(regular, 2);
9888   *regular = ((DM_Plex *)dm->data)->regularRefinement;
9889   PetscFunctionReturn(PETSC_SUCCESS);
9890 }
9891 
9892 /*@
9893   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9894 
9895   Input Parameters:
9896 + dm      - The `DMPLEX` object
9897 - regular - The flag
9898 
9899   Level: intermediate
9900 
9901 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
9902 @*/
9903 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
9904 {
9905   PetscFunctionBegin;
9906   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9907   ((DM_Plex *)dm->data)->regularRefinement = regular;
9908   PetscFunctionReturn(PETSC_SUCCESS);
9909 }
9910 
9911 /*@
9912   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
9913   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
9914 
9915   Not Collective
9916 
9917   Input Parameter:
9918 . dm - The `DMPLEX` object
9919 
9920   Output Parameters:
9921 + anchorSection - If not `NULL`, set to the section describing which points anchor the constrained points.
9922 - anchorIS      - If not `NULL`, set to the list of anchors indexed by `anchorSection`
9923 
9924   Level: intermediate
9925 
9926 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
9927 @*/
9928 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
9929 {
9930   DM_Plex *plex = (DM_Plex *)dm->data;
9931 
9932   PetscFunctionBegin;
9933   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9934   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
9935   if (anchorSection) *anchorSection = plex->anchorSection;
9936   if (anchorIS) *anchorIS = plex->anchorIS;
9937   PetscFunctionReturn(PETSC_SUCCESS);
9938 }
9939 
9940 /*@
9941   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.
9942 
9943   Collective
9944 
9945   Input Parameters:
9946 + dm            - The `DMPLEX` object
9947 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
9948                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
9949 - anchorIS      - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
9950 
9951   Level: intermediate
9952 
9953   Notes:
9954   Unlike boundary conditions, when a point's degrees of freedom in a section are constrained to
9955   an outside value, the anchor constraints set a point's degrees of freedom to be a linear
9956   combination of other points' degrees of freedom.
9957 
9958   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
9959   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
9960 
9961   The reference counts of `anchorSection` and `anchorIS` are incremented.
9962 
9963 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9964 @*/
9965 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
9966 {
9967   DM_Plex    *plex = (DM_Plex *)dm->data;
9968   PetscMPIInt result;
9969 
9970   PetscFunctionBegin;
9971   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9972   if (anchorSection) {
9973     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
9974     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
9975     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
9976   }
9977   if (anchorIS) {
9978     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
9979     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
9980     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
9981   }
9982 
9983   PetscCall(PetscObjectReference((PetscObject)anchorSection));
9984   PetscCall(PetscSectionDestroy(&plex->anchorSection));
9985   plex->anchorSection = anchorSection;
9986 
9987   PetscCall(PetscObjectReference((PetscObject)anchorIS));
9988   PetscCall(ISDestroy(&plex->anchorIS));
9989   plex->anchorIS = anchorIS;
9990 
9991   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
9992     PetscInt        size, a, pStart, pEnd;
9993     const PetscInt *anchors;
9994 
9995     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
9996     PetscCall(ISGetLocalSize(anchorIS, &size));
9997     PetscCall(ISGetIndices(anchorIS, &anchors));
9998     for (a = 0; a < size; a++) {
9999       PetscInt p;
10000 
10001       p = anchors[a];
10002       if (p >= pStart && p < pEnd) {
10003         PetscInt dof;
10004 
10005         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10006         if (dof) {
10007           PetscCall(ISRestoreIndices(anchorIS, &anchors));
10008           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
10009         }
10010       }
10011     }
10012     PetscCall(ISRestoreIndices(anchorIS, &anchors));
10013   }
10014   /* reset the generic constraints */
10015   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
10016   PetscFunctionReturn(PETSC_SUCCESS);
10017 }
10018 
10019 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
10020 {
10021   PetscSection anchorSection;
10022   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
10023 
10024   PetscFunctionBegin;
10025   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10026   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10027   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
10028   PetscCall(PetscSectionGetNumFields(section, &numFields));
10029   if (numFields) {
10030     PetscInt f;
10031     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
10032 
10033     for (f = 0; f < numFields; f++) {
10034       PetscInt numComp;
10035 
10036       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
10037       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
10038     }
10039   }
10040   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10041   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10042   pStart = PetscMax(pStart, sStart);
10043   pEnd   = PetscMin(pEnd, sEnd);
10044   pEnd   = PetscMax(pStart, pEnd);
10045   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
10046   for (p = pStart; p < pEnd; p++) {
10047     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10048     if (dof) {
10049       PetscCall(PetscSectionGetDof(section, p, &dof));
10050       PetscCall(PetscSectionSetDof(*cSec, p, dof));
10051       for (f = 0; f < numFields; f++) {
10052         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
10053         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
10054       }
10055     }
10056   }
10057   PetscCall(PetscSectionSetUp(*cSec));
10058   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
10059   PetscFunctionReturn(PETSC_SUCCESS);
10060 }
10061 
10062 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
10063 {
10064   PetscSection    aSec;
10065   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
10066   const PetscInt *anchors;
10067   PetscInt        numFields, f;
10068   IS              aIS;
10069   MatType         mtype;
10070   PetscBool       iscuda, iskokkos;
10071 
10072   PetscFunctionBegin;
10073   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10074   PetscCall(PetscSectionGetStorageSize(cSec, &m));
10075   PetscCall(PetscSectionGetStorageSize(section, &n));
10076   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
10077   PetscCall(MatSetSizes(*cMat, m, n, m, n));
10078   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
10079   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
10080   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
10081   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
10082   if (iscuda) mtype = MATSEQAIJCUSPARSE;
10083   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
10084   else mtype = MATSEQAIJ;
10085   PetscCall(MatSetType(*cMat, mtype));
10086   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
10087   PetscCall(ISGetIndices(aIS, &anchors));
10088   /* cSec will be a subset of aSec and section */
10089   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
10090   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10091   PetscCall(PetscMalloc1(m + 1, &i));
10092   i[0] = 0;
10093   PetscCall(PetscSectionGetNumFields(section, &numFields));
10094   for (p = pStart; p < pEnd; p++) {
10095     PetscInt rDof, rOff, r;
10096 
10097     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10098     if (!rDof) continue;
10099     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10100     if (numFields) {
10101       for (f = 0; f < numFields; f++) {
10102         annz = 0;
10103         for (r = 0; r < rDof; r++) {
10104           a = anchors[rOff + r];
10105           if (a < sStart || a >= sEnd) continue;
10106           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10107           annz += aDof;
10108         }
10109         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10110         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
10111         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10112       }
10113     } else {
10114       annz = 0;
10115       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10116       for (q = 0; q < dof; q++) {
10117         a = anchors[rOff + q];
10118         if (a < sStart || a >= sEnd) continue;
10119         PetscCall(PetscSectionGetDof(section, a, &aDof));
10120         annz += aDof;
10121       }
10122       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10123       PetscCall(PetscSectionGetOffset(cSec, p, &off));
10124       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10125     }
10126   }
10127   nnz = i[m];
10128   PetscCall(PetscMalloc1(nnz, &j));
10129   offset = 0;
10130   for (p = pStart; p < pEnd; p++) {
10131     if (numFields) {
10132       for (f = 0; f < numFields; f++) {
10133         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10134         for (q = 0; q < dof; q++) {
10135           PetscInt rDof, rOff, r;
10136           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10137           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10138           for (r = 0; r < rDof; r++) {
10139             PetscInt s;
10140 
10141             a = anchors[rOff + r];
10142             if (a < sStart || a >= sEnd) continue;
10143             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10144             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
10145             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10146           }
10147         }
10148       }
10149     } else {
10150       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10151       for (q = 0; q < dof; q++) {
10152         PetscInt rDof, rOff, r;
10153         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10154         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10155         for (r = 0; r < rDof; r++) {
10156           PetscInt s;
10157 
10158           a = anchors[rOff + r];
10159           if (a < sStart || a >= sEnd) continue;
10160           PetscCall(PetscSectionGetDof(section, a, &aDof));
10161           PetscCall(PetscSectionGetOffset(section, a, &aOff));
10162           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10163         }
10164       }
10165     }
10166   }
10167   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
10168   PetscCall(PetscFree(i));
10169   PetscCall(PetscFree(j));
10170   PetscCall(ISRestoreIndices(aIS, &anchors));
10171   PetscFunctionReturn(PETSC_SUCCESS);
10172 }
10173 
10174 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
10175 {
10176   DM_Plex     *plex = (DM_Plex *)dm->data;
10177   PetscSection anchorSection, section, cSec;
10178   Mat          cMat;
10179 
10180   PetscFunctionBegin;
10181   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10182   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10183   if (anchorSection) {
10184     PetscInt Nf;
10185 
10186     PetscCall(DMGetLocalSection(dm, &section));
10187     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
10188     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
10189     PetscCall(DMGetNumFields(dm, &Nf));
10190     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
10191     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
10192     PetscCall(PetscSectionDestroy(&cSec));
10193     PetscCall(MatDestroy(&cMat));
10194   }
10195   PetscFunctionReturn(PETSC_SUCCESS);
10196 }
10197 
10198 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10199 {
10200   IS           subis;
10201   PetscSection section, subsection;
10202 
10203   PetscFunctionBegin;
10204   PetscCall(DMGetLocalSection(dm, &section));
10205   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10206   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10207   /* Create subdomain */
10208   PetscCall(DMPlexFilter(dm, label, value, subdm));
10209   /* Create submodel */
10210   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10211   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10212   PetscCall(DMSetLocalSection(*subdm, subsection));
10213   PetscCall(PetscSectionDestroy(&subsection));
10214   PetscCall(DMCopyDisc(dm, *subdm));
10215   /* Create map from submodel to global model */
10216   if (is) {
10217     PetscSection    sectionGlobal, subsectionGlobal;
10218     IS              spIS;
10219     const PetscInt *spmap;
10220     PetscInt       *subIndices;
10221     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10222     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10223 
10224     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10225     PetscCall(ISGetIndices(spIS, &spmap));
10226     PetscCall(PetscSectionGetNumFields(section, &Nf));
10227     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10228     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10229     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10230     for (p = pStart; p < pEnd; ++p) {
10231       PetscInt gdof, pSubSize = 0;
10232 
10233       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10234       if (gdof > 0) {
10235         for (f = 0; f < Nf; ++f) {
10236           PetscInt fdof, fcdof;
10237 
10238           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10239           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10240           pSubSize += fdof - fcdof;
10241         }
10242         subSize += pSubSize;
10243         if (pSubSize) {
10244           if (bs < 0) {
10245             bs = pSubSize;
10246           } else if (bs != pSubSize) {
10247             /* Layout does not admit a pointwise block size */
10248             bs = 1;
10249           }
10250         }
10251       }
10252     }
10253     /* Must have same blocksize on all procs (some might have no points) */
10254     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
10255     bsLocal[1] = bs;
10256     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10257     if (bsMinMax[0] != bsMinMax[1]) {
10258       bs = 1;
10259     } else {
10260       bs = bsMinMax[0];
10261     }
10262     PetscCall(PetscMalloc1(subSize, &subIndices));
10263     for (p = pStart; p < pEnd; ++p) {
10264       PetscInt gdof, goff;
10265 
10266       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10267       if (gdof > 0) {
10268         const PetscInt point = spmap[p];
10269 
10270         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10271         for (f = 0; f < Nf; ++f) {
10272           PetscInt fdof, fcdof, fc, f2, poff = 0;
10273 
10274           /* Can get rid of this loop by storing field information in the global section */
10275           for (f2 = 0; f2 < f; ++f2) {
10276             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10277             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10278             poff += fdof - fcdof;
10279           }
10280           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10281           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10282           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10283         }
10284       }
10285     }
10286     PetscCall(ISRestoreIndices(spIS, &spmap));
10287     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10288     if (bs > 1) {
10289       /* We need to check that the block size does not come from non-contiguous fields */
10290       PetscInt i, j, set = 1;
10291       for (i = 0; i < subSize; i += bs) {
10292         for (j = 0; j < bs; ++j) {
10293           if (subIndices[i + j] != subIndices[i] + j) {
10294             set = 0;
10295             break;
10296           }
10297         }
10298       }
10299       if (set) PetscCall(ISSetBlockSize(*is, bs));
10300     }
10301     /* Attach nullspace */
10302     for (f = 0; f < Nf; ++f) {
10303       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10304       if ((*subdm)->nullspaceConstructors[f]) break;
10305     }
10306     if (f < Nf) {
10307       MatNullSpace nullSpace;
10308       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10309 
10310       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10311       PetscCall(MatNullSpaceDestroy(&nullSpace));
10312     }
10313   }
10314   PetscFunctionReturn(PETSC_SUCCESS);
10315 }
10316 
10317 /*@
10318   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10319 
10320   Input Parameters:
10321 + dm    - The `DM`
10322 - dummy - unused argument
10323 
10324   Options Database Key:
10325 . -dm_plex_monitor_throughput - Activate the monitor
10326 
10327   Level: developer
10328 
10329 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10330 @*/
10331 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10332 {
10333   PetscLogHandler default_handler;
10334 
10335   PetscFunctionBegin;
10336   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10337   PetscCall(PetscLogGetDefaultHandler(&default_handler));
10338   if (default_handler) {
10339     PetscLogEvent      event;
10340     PetscEventPerfInfo eventInfo;
10341     PetscReal          cellRate, flopRate;
10342     PetscInt           cStart, cEnd, Nf, N;
10343     const char        *name;
10344 
10345     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10346     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10347     PetscCall(DMGetNumFields(dm, &Nf));
10348     PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10349     PetscCall(PetscLogEventGetPerfInfo(PETSC_DEFAULT, event, &eventInfo));
10350     N        = (cEnd - cStart) * Nf * eventInfo.count;
10351     flopRate = eventInfo.flops / eventInfo.time;
10352     cellRate = N / eventInfo.time;
10353     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)));
10354   } else {
10355     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.");
10356   }
10357   PetscFunctionReturn(PETSC_SUCCESS);
10358 }
10359