xref: /petsc/src/dm/impls/plex/plex.c (revision 9140fee14acbea959c6ed74f4836a1a89f708038)
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 function requires that tensor cells are ordered last.
79 
80 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetCellTypeStratum()`
81 @*/
82 PetscErrorCode DMPlexGetSimplexOrBoxCells(DM dm, PetscInt height, PetscInt *cStart, PetscInt *cEnd)
83 {
84   DMLabel         ctLabel;
85   IS              valueIS;
86   const PetscInt *ctypes;
87   PetscInt        Nct, cS = PETSC_MAX_INT, cE = 0;
88 
89   PetscFunctionBegin;
90   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
91   PetscCall(DMLabelGetValueIS(ctLabel, &valueIS));
92   PetscCall(ISGetLocalSize(valueIS, &Nct));
93   PetscCall(ISGetIndices(valueIS, &ctypes));
94   if (!Nct) cS = cE = 0;
95   for (PetscInt t = 0; t < Nct; ++t) {
96     const PetscInt ct = ctypes[t];
97     PetscInt       ctS, ctE, ht;
98 
99     if (ct == DM_POLYTOPE_UNKNOWN) {
100       // If any cells are not typed, just use all cells
101       PetscCall(DMPlexGetHeightStratum(dm, PetscMax(height, 0), cStart, cEnd));
102       break;
103     }
104     if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_POINT_PRISM_TENSOR || ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) continue;
105     PetscCall(DMLabelGetStratumBounds(ctLabel, ct, &ctS, &ctE));
106     if (ctS >= ctE) continue;
107     // Check that a point has the right height
108     PetscCall(DMPlexGetPointHeight(dm, ctS, &ht));
109     if (ht != height) continue;
110     cS = PetscMin(cS, ctS);
111     cE = PetscMax(cE, ctE);
112   }
113   PetscCall(ISDestroy(&valueIS));
114   // Reset label for fast lookup
115   PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
116   if (cStart) *cStart = cS;
117   if (cEnd) *cEnd = cE;
118   PetscFunctionReturn(PETSC_SUCCESS);
119 }
120 
121 PetscErrorCode DMPlexGetFieldTypes_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *types, PetscInt **ssStart, PetscInt **ssEnd, PetscViewerVTKFieldType **sft)
122 {
123   PetscInt                 cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd, c, depth, cellHeight, t;
124   PetscInt                *sStart, *sEnd;
125   PetscViewerVTKFieldType *ft;
126   PetscInt                 vcdof[DM_NUM_POLYTOPES + 1], globalvcdof[DM_NUM_POLYTOPES + 1];
127   DMLabel                  depthLabel, ctLabel;
128 
129   PetscFunctionBegin;
130 
131   /* the vcdof and globalvcdof are sized to allow every polytope type and simple vertex at DM_NUM_POLYTOPES */
132   PetscCall(PetscArrayzero(vcdof, DM_NUM_POLYTOPES + 1));
133   PetscCall(DMGetCoordinateDim(dm, &cdim));
134   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
135   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
136   if (field >= 0) {
137     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[DM_NUM_POLYTOPES]));
138   } else {
139     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[DM_NUM_POLYTOPES]));
140   }
141 
142   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
143   PetscCall(DMPlexGetDepth(dm, &depth));
144   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
145   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
146   for (c = 0; c < DM_NUM_POLYTOPES; ++c) {
147     const DMPolytopeType ict = (DMPolytopeType)c;
148     PetscInt             dep;
149 
150     if (ict == DM_POLYTOPE_FV_GHOST) continue;
151     PetscCall(DMLabelGetStratumBounds(ctLabel, ict, &cStart, &cEnd));
152     if (pStart >= 0) {
153       PetscCall(DMLabelGetValue(depthLabel, cStart, &dep));
154       if (dep != depth - cellHeight) continue;
155     }
156     if (field >= 0) {
157       if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[c]));
158     } else {
159       if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[c]));
160     }
161     PetscCall(MPIU_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
162   }
163 
164   PetscCall(MPIU_Allreduce(vcdof, globalvcdof, DM_NUM_POLYTOPES + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
165   *types = 0;
166 
167   for (c = 0; c < DM_NUM_POLYTOPES + 1; ++c) {
168     if (globalvcdof[c]) ++(*types);
169   }
170 
171   PetscCall(PetscMalloc3(*types, &sStart, *types, &sEnd, *types, &ft));
172   t = 0;
173   if (globalvcdof[DM_NUM_POLYTOPES]) {
174     sStart[t] = vStart;
175     sEnd[t]   = vEnd;
176     ft[t]     = (globalvcdof[t] == cdim) ? PETSC_VTK_POINT_VECTOR_FIELD : PETSC_VTK_POINT_FIELD;
177     ++t;
178   }
179 
180   for (c = 0; c < DM_NUM_POLYTOPES; ++c) {
181     if (globalvcdof[c]) {
182       const DMPolytopeType ict = (DMPolytopeType)c;
183 
184       PetscCall(DMLabelGetStratumBounds(ctLabel, ict, &cStart, &cEnd));
185       sStart[t] = cStart;
186       sEnd[t]   = cEnd;
187       ft[t]     = (globalvcdof[c] == cdim) ? PETSC_VTK_CELL_VECTOR_FIELD : PETSC_VTK_CELL_FIELD;
188       ++t;
189     }
190   }
191 
192   if (!(*types)) {
193     if (field >= 0) {
194       const char *fieldname;
195 
196       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
197       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
198     } else {
199       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
200     }
201   }
202 
203   *ssStart = sStart;
204   *ssEnd   = sEnd;
205   *sft     = ft;
206   PetscFunctionReturn(PETSC_SUCCESS);
207 }
208 
209 PetscErrorCode DMPlexRestoreFieldTypes_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *types, PetscInt **sStart, PetscInt **sEnd, PetscViewerVTKFieldType **ft)
210 {
211   PetscFunctionBegin;
212   PetscCall(PetscFree3(*sStart, *sEnd, *ft));
213   PetscFunctionReturn(PETSC_SUCCESS);
214 }
215 
216 PetscErrorCode DMPlexGetFieldType_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *sStart, PetscInt *sEnd, PetscViewerVTKFieldType *ft)
217 {
218   PetscInt cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd;
219   PetscInt vcdof[2] = {0, 0}, globalvcdof[2];
220 
221   PetscFunctionBegin;
222   *ft = PETSC_VTK_INVALID;
223   PetscCall(DMGetCoordinateDim(dm, &cdim));
224   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
225   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
226   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
227   if (field >= 0) {
228     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[0]));
229     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[1]));
230   } else {
231     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[0]));
232     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[1]));
233   }
234   PetscCall(MPIU_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
235   if (globalvcdof[0]) {
236     *sStart = vStart;
237     *sEnd   = vEnd;
238     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
239     else *ft = PETSC_VTK_POINT_FIELD;
240   } else if (globalvcdof[1]) {
241     *sStart = cStart;
242     *sEnd   = cEnd;
243     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
244     else *ft = PETSC_VTK_CELL_FIELD;
245   } else {
246     if (field >= 0) {
247       const char *fieldname;
248 
249       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
250       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
251     } else {
252       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
253     }
254   }
255   PetscFunctionReturn(PETSC_SUCCESS);
256 }
257 
258 /*@
259   DMPlexVecView1D - Plot many 1D solutions on the same line graph
260 
261   Collective
262 
263   Input Parameters:
264 + dm     - The `DMPLEX` object
265 . n      - The number of vectors
266 . u      - The array of local vectors
267 - viewer - The `PetscViewer`
268 
269   Level: advanced
270 
271 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `VecViewFromOptions()`, `VecView()`
272 @*/
273 PetscErrorCode DMPlexVecView1D(DM dm, PetscInt n, Vec u[], PetscViewer viewer)
274 {
275   PetscDS            ds;
276   PetscDraw          draw = NULL;
277   PetscDrawLG        lg;
278   Vec                coordinates;
279   const PetscScalar *coords, **sol;
280   PetscReal         *vals;
281   PetscInt          *Nc;
282   PetscInt           Nf, f, c, Nl, l, i, vStart, vEnd, v;
283   char             **names;
284 
285   PetscFunctionBegin;
286   PetscCall(DMGetDS(dm, &ds));
287   PetscCall(PetscDSGetNumFields(ds, &Nf));
288   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
289   PetscCall(PetscDSGetComponents(ds, &Nc));
290 
291   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
292   if (!draw) PetscFunctionReturn(PETSC_SUCCESS);
293   PetscCall(PetscDrawLGCreate(draw, n * Nl, &lg));
294 
295   PetscCall(PetscMalloc3(n, &sol, n * Nl, &names, n * Nl, &vals));
296   for (i = 0, l = 0; i < n; ++i) {
297     const char *vname;
298 
299     PetscCall(PetscObjectGetName((PetscObject)u[i], &vname));
300     for (f = 0; f < Nf; ++f) {
301       PetscObject disc;
302       const char *fname;
303       char        tmpname[PETSC_MAX_PATH_LEN];
304 
305       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
306       /* TODO Create names for components */
307       for (c = 0; c < Nc[f]; ++c, ++l) {
308         PetscCall(PetscObjectGetName(disc, &fname));
309         PetscCall(PetscStrncpy(tmpname, vname, sizeof(tmpname)));
310         PetscCall(PetscStrlcat(tmpname, ":", sizeof(tmpname)));
311         PetscCall(PetscStrlcat(tmpname, fname, sizeof(tmpname)));
312         PetscCall(PetscStrallocpy(tmpname, &names[l]));
313       }
314     }
315   }
316   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *)names));
317   /* Just add P_1 support for now */
318   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
319   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
320   PetscCall(VecGetArrayRead(coordinates, &coords));
321   for (i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
322   for (v = vStart; v < vEnd; ++v) {
323     PetscScalar *x, *svals;
324 
325     PetscCall(DMPlexPointLocalRead(dm, v, coords, &x));
326     for (i = 0; i < n; ++i) {
327       PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
328       for (l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
329     }
330     PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
331   }
332   PetscCall(VecRestoreArrayRead(coordinates, &coords));
333   for (i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
334   for (l = 0; l < n * Nl; ++l) PetscCall(PetscFree(names[l]));
335   PetscCall(PetscFree3(sol, names, vals));
336 
337   PetscCall(PetscDrawLGDraw(lg));
338   PetscCall(PetscDrawLGDestroy(&lg));
339   PetscFunctionReturn(PETSC_SUCCESS);
340 }
341 
342 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer)
343 {
344   DM dm;
345 
346   PetscFunctionBegin;
347   PetscCall(VecGetDM(u, &dm));
348   PetscCall(DMPlexVecView1D(dm, 1, &u, viewer));
349   PetscFunctionReturn(PETSC_SUCCESS);
350 }
351 
352 static PetscErrorCode VecView_Plex_Local_Draw_2D(Vec v, PetscViewer viewer)
353 {
354   DM                 dm;
355   PetscSection       s;
356   PetscDraw          draw, popup;
357   DM                 cdm;
358   PetscSection       coordSection;
359   Vec                coordinates;
360   const PetscScalar *array;
361   PetscReal          lbound[3], ubound[3];
362   PetscReal          vbound[2], time;
363   PetscBool          flg;
364   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
365   const char        *name;
366   char               title[PETSC_MAX_PATH_LEN];
367 
368   PetscFunctionBegin;
369   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
370   PetscCall(VecGetDM(v, &dm));
371   PetscCall(DMGetCoordinateDim(dm, &dim));
372   PetscCall(DMGetLocalSection(dm, &s));
373   PetscCall(PetscSectionGetNumFields(s, &Nf));
374   PetscCall(DMGetCoarsenLevel(dm, &level));
375   PetscCall(DMGetCoordinateDM(dm, &cdm));
376   PetscCall(DMGetLocalSection(cdm, &coordSection));
377   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
378   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
379   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
380 
381   PetscCall(PetscObjectGetName((PetscObject)v, &name));
382   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
383 
384   PetscCall(VecGetLocalSize(coordinates, &N));
385   PetscCall(DMGetBoundingBox(dm, lbound, ubound));
386   PetscCall(PetscDrawClear(draw));
387 
388   /* Could implement something like DMDASelectFields() */
389   for (f = 0; f < Nf; ++f) {
390     DM          fdm = dm;
391     Vec         fv  = v;
392     IS          fis;
393     char        prefix[PETSC_MAX_PATH_LEN];
394     const char *fname;
395 
396     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
397     PetscCall(PetscSectionGetFieldName(s, f, &fname));
398 
399     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix, sizeof(prefix)));
400     else prefix[0] = '\0';
401     if (Nf > 1) {
402       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
403       PetscCall(VecGetSubVector(v, fis, &fv));
404       PetscCall(PetscStrlcat(prefix, fname, sizeof(prefix)));
405       PetscCall(PetscStrlcat(prefix, "_", sizeof(prefix)));
406     }
407     for (comp = 0; comp < Nc; ++comp, ++w) {
408       PetscInt nmax = 2;
409 
410       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
411       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
412       else PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
413       PetscCall(PetscDrawSetTitle(draw, title));
414 
415       /* TODO Get max and min only for this component */
416       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
417       if (!flg) {
418         PetscCall(VecMin(fv, NULL, &vbound[0]));
419         PetscCall(VecMax(fv, NULL, &vbound[1]));
420         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
421       }
422 
423       PetscCall(PetscDrawGetPopup(draw, &popup));
424       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
425       PetscCall(PetscDrawSetCoordinates(draw, lbound[0], lbound[1], ubound[0], ubound[1]));
426       PetscCall(VecGetArrayRead(fv, &array));
427       for (c = cStart; c < cEnd; ++c) {
428         PetscScalar       *coords = NULL, *a = NULL;
429         const PetscScalar *coords_arr;
430         PetscBool          isDG;
431         PetscInt           numCoords, color[4] = {-1, -1, -1, -1};
432 
433         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
434         if (a) {
435           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
436           color[1] = color[2] = color[3] = color[0];
437         } else {
438           PetscScalar *vals = NULL;
439           PetscInt     numVals, va;
440 
441           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
442           PetscCheck(numVals % Nc == 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "The number of components %" PetscInt_FMT " does not divide the number of values in the closure %" PetscInt_FMT, Nc, numVals);
443           switch (numVals / Nc) {
444           case 3: /* P1 Triangle */
445           case 4: /* P1 Quadrangle */
446             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
447             break;
448           case 6: /* P2 Triangle */
449           case 8: /* P2 Quadrangle */
450             for (va = 0; va < numVals / (Nc * 2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp + numVals / (Nc * 2)]), vbound[0], vbound[1]);
451             break;
452           default:
453             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals / Nc);
454           }
455           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
456         }
457         PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
458         switch (numCoords) {
459         case 6:
460         case 12: /* Localized triangle */
461           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
462           break;
463         case 8:
464         case 16: /* Localized quadrilateral */
465           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
466           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), color[2], color[3], color[0]));
467           break;
468         default:
469           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
470         }
471         PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
472       }
473       PetscCall(VecRestoreArrayRead(fv, &array));
474       PetscCall(PetscDrawFlush(draw));
475       PetscCall(PetscDrawPause(draw));
476       PetscCall(PetscDrawSave(draw));
477     }
478     if (Nf > 1) {
479       PetscCall(VecRestoreSubVector(v, fis, &fv));
480       PetscCall(ISDestroy(&fis));
481       PetscCall(DMDestroy(&fdm));
482     }
483   }
484   PetscFunctionReturn(PETSC_SUCCESS);
485 }
486 
487 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
488 {
489   DM        dm;
490   PetscDraw draw;
491   PetscInt  dim;
492   PetscBool isnull;
493 
494   PetscFunctionBegin;
495   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
496   PetscCall(PetscDrawIsNull(draw, &isnull));
497   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
498 
499   PetscCall(VecGetDM(v, &dm));
500   PetscCall(DMGetCoordinateDim(dm, &dim));
501   switch (dim) {
502   case 1:
503     PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));
504     break;
505   case 2:
506     PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));
507     break;
508   default:
509     SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
510   }
511   PetscFunctionReturn(PETSC_SUCCESS);
512 }
513 
514 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
515 {
516   DM                      dm;
517   Vec                     locv;
518   const char             *name;
519   PetscSection            section;
520   PetscInt                pStart, pEnd;
521   PetscInt                numFields;
522   PetscViewerVTKFieldType ft;
523 
524   PetscFunctionBegin;
525   PetscCall(VecGetDM(v, &dm));
526   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
527   PetscCall(PetscObjectGetName((PetscObject)v, &name));
528   PetscCall(PetscObjectSetName((PetscObject)locv, name));
529   PetscCall(VecCopy(v, locv));
530   PetscCall(DMGetLocalSection(dm, &section));
531   PetscCall(PetscSectionGetNumFields(section, &numFields));
532   if (!numFields) {
533     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
534     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE, (PetscObject)locv));
535   } else {
536     PetscInt f;
537 
538     for (f = 0; f < numFields; f++) {
539       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
540       if (ft == PETSC_VTK_INVALID) continue;
541       PetscCall(PetscObjectReference((PetscObject)locv));
542       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE, (PetscObject)locv));
543     }
544     PetscCall(VecDestroy(&locv));
545   }
546   PetscFunctionReturn(PETSC_SUCCESS);
547 }
548 
549 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
550 {
551   DM        dm;
552   PetscBool isvtk, ishdf5, isdraw, isglvis, iscgns;
553 
554   PetscFunctionBegin;
555   PetscCall(VecGetDM(v, &dm));
556   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
557   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
558   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
559   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
560   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
561   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
562   if (isvtk || ishdf5 || isdraw || isglvis || iscgns) {
563     PetscInt    i, numFields;
564     PetscObject fe;
565     PetscBool   fem  = PETSC_FALSE;
566     Vec         locv = v;
567     const char *name;
568     PetscInt    step;
569     PetscReal   time;
570 
571     PetscCall(DMGetNumFields(dm, &numFields));
572     for (i = 0; i < numFields; i++) {
573       PetscCall(DMGetField(dm, i, NULL, &fe));
574       if (fe->classid == PETSCFE_CLASSID) {
575         fem = PETSC_TRUE;
576         break;
577       }
578     }
579     if (fem) {
580       PetscObject isZero;
581 
582       PetscCall(DMGetLocalVector(dm, &locv));
583       PetscCall(PetscObjectGetName((PetscObject)v, &name));
584       PetscCall(PetscObjectSetName((PetscObject)locv, name));
585       PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
586       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
587       PetscCall(VecCopy(v, locv));
588       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
589       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
590     }
591     if (isvtk) {
592       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
593     } else if (ishdf5) {
594 #if defined(PETSC_HAVE_HDF5)
595       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
596 #else
597       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
598 #endif
599     } else if (isdraw) {
600       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
601     } else if (isglvis) {
602       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
603       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
604       PetscCall(VecView_GLVis(locv, viewer));
605     } else if (iscgns) {
606 #if defined(PETSC_HAVE_CGNS)
607       PetscCall(VecView_Plex_Local_CGNS(locv, viewer));
608 #else
609       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
610 #endif
611     }
612     if (fem) {
613       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
614       PetscCall(DMRestoreLocalVector(dm, &locv));
615     }
616   } else {
617     PetscBool isseq;
618 
619     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
620     if (isseq) PetscCall(VecView_Seq(v, viewer));
621     else PetscCall(VecView_MPI(v, viewer));
622   }
623   PetscFunctionReturn(PETSC_SUCCESS);
624 }
625 
626 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
627 {
628   DM        dm;
629   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii, iscgns;
630 
631   PetscFunctionBegin;
632   PetscCall(VecGetDM(v, &dm));
633   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
634   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
635   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
636   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
637   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
638   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
639   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
640   if (isvtk || isdraw || isglvis || iscgns) {
641     Vec         locv;
642     PetscObject isZero;
643     const char *name;
644 
645     PetscCall(DMGetLocalVector(dm, &locv));
646     PetscCall(PetscObjectGetName((PetscObject)v, &name));
647     PetscCall(PetscObjectSetName((PetscObject)locv, name));
648     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
649     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
650     PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
651     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
652     PetscCall(VecView_Plex_Local(locv, viewer));
653     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
654     PetscCall(DMRestoreLocalVector(dm, &locv));
655   } else if (ishdf5) {
656 #if defined(PETSC_HAVE_HDF5)
657     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
658 #else
659     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
660 #endif
661   } else if (isexodusii) {
662 #if defined(PETSC_HAVE_EXODUSII)
663     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
664 #else
665     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
666 #endif
667   } else {
668     PetscBool isseq;
669 
670     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
671     if (isseq) PetscCall(VecView_Seq(v, viewer));
672     else PetscCall(VecView_MPI(v, viewer));
673   }
674   PetscFunctionReturn(PETSC_SUCCESS);
675 }
676 
677 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
678 {
679   DM                dm;
680   MPI_Comm          comm;
681   PetscViewerFormat format;
682   Vec               v;
683   PetscBool         isvtk, ishdf5;
684 
685   PetscFunctionBegin;
686   PetscCall(VecGetDM(originalv, &dm));
687   PetscCall(PetscObjectGetComm((PetscObject)originalv, &comm));
688   PetscCheck(dm, comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
689   PetscCall(PetscViewerGetFormat(viewer, &format));
690   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
691   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
692   if (format == PETSC_VIEWER_NATIVE) {
693     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
694     /* this need a better fix */
695     if (dm->useNatural) {
696       if (dm->sfNatural) {
697         const char *vecname;
698         PetscInt    n, nroots;
699 
700         PetscCall(VecGetLocalSize(originalv, &n));
701         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
702         if (n == nroots) {
703           PetscCall(DMPlexCreateNaturalVector(dm, &v));
704           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
705           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
706           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
707           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
708         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
709       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
710     } else v = originalv;
711   } else v = originalv;
712 
713   if (ishdf5) {
714 #if defined(PETSC_HAVE_HDF5)
715     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
716 #else
717     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
718 #endif
719   } else if (isvtk) {
720     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
721   } else {
722     PetscBool isseq;
723 
724     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
725     if (isseq) PetscCall(VecView_Seq(v, viewer));
726     else PetscCall(VecView_MPI(v, viewer));
727   }
728   if (v != originalv) PetscCall(VecDestroy(&v));
729   PetscFunctionReturn(PETSC_SUCCESS);
730 }
731 
732 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
733 {
734   DM        dm;
735   PetscBool ishdf5;
736 
737   PetscFunctionBegin;
738   PetscCall(VecGetDM(v, &dm));
739   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
740   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
741   if (ishdf5) {
742     DM          dmBC;
743     Vec         gv;
744     const char *name;
745 
746     PetscCall(DMGetOutputDM(dm, &dmBC));
747     PetscCall(DMGetGlobalVector(dmBC, &gv));
748     PetscCall(PetscObjectGetName((PetscObject)v, &name));
749     PetscCall(PetscObjectSetName((PetscObject)gv, name));
750     PetscCall(VecLoad_Default(gv, viewer));
751     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
752     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
753     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
754   } else PetscCall(VecLoad_Default(v, viewer));
755   PetscFunctionReturn(PETSC_SUCCESS);
756 }
757 
758 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
759 {
760   DM        dm;
761   PetscBool ishdf5, isexodusii;
762 
763   PetscFunctionBegin;
764   PetscCall(VecGetDM(v, &dm));
765   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
766   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
767   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
768   if (ishdf5) {
769 #if defined(PETSC_HAVE_HDF5)
770     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
771 #else
772     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
773 #endif
774   } else if (isexodusii) {
775 #if defined(PETSC_HAVE_EXODUSII)
776     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
777 #else
778     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
779 #endif
780   } else PetscCall(VecLoad_Default(v, viewer));
781   PetscFunctionReturn(PETSC_SUCCESS);
782 }
783 
784 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
785 {
786   DM                dm;
787   PetscViewerFormat format;
788   PetscBool         ishdf5;
789 
790   PetscFunctionBegin;
791   PetscCall(VecGetDM(originalv, &dm));
792   PetscCheck(dm, PetscObjectComm((PetscObject)originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
793   PetscCall(PetscViewerGetFormat(viewer, &format));
794   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
795   if (format == PETSC_VIEWER_NATIVE) {
796     if (dm->useNatural) {
797       if (dm->sfNatural) {
798         if (ishdf5) {
799 #if defined(PETSC_HAVE_HDF5)
800           Vec         v;
801           const char *vecname;
802 
803           PetscCall(DMPlexCreateNaturalVector(dm, &v));
804           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
805           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
806           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
807           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
808           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
809           PetscCall(VecDestroy(&v));
810 #else
811           SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
812 #endif
813         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
814       }
815     } else PetscCall(VecLoad_Default(originalv, viewer));
816   }
817   PetscFunctionReturn(PETSC_SUCCESS);
818 }
819 
820 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
821 {
822   PetscSection       coordSection;
823   Vec                coordinates;
824   DMLabel            depthLabel, celltypeLabel;
825   const char        *name[4];
826   const PetscScalar *a;
827   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
828 
829   PetscFunctionBegin;
830   PetscCall(DMGetDimension(dm, &dim));
831   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
832   PetscCall(DMGetCoordinateSection(dm, &coordSection));
833   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
834   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
835   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
836   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
837   PetscCall(VecGetArrayRead(coordinates, &a));
838   name[0]       = "vertex";
839   name[1]       = "edge";
840   name[dim - 1] = "face";
841   name[dim]     = "cell";
842   for (c = cStart; c < cEnd; ++c) {
843     PetscInt *closure = NULL;
844     PetscInt  closureSize, cl, ct;
845 
846     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
847     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
848     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
849     PetscCall(PetscViewerASCIIPushTab(viewer));
850     for (cl = 0; cl < closureSize * 2; cl += 2) {
851       PetscInt point = closure[cl], depth, dof, off, d, p;
852 
853       if ((point < pStart) || (point >= pEnd)) continue;
854       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
855       if (!dof) continue;
856       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
857       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
858       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
859       for (p = 0; p < dof / dim; ++p) {
860         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
861         for (d = 0; d < dim; ++d) {
862           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
863           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double)PetscRealPart(a[off + p * dim + d])));
864         }
865         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
866       }
867       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
868     }
869     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
870     PetscCall(PetscViewerASCIIPopTab(viewer));
871   }
872   PetscCall(VecRestoreArrayRead(coordinates, &a));
873   PetscFunctionReturn(PETSC_SUCCESS);
874 }
875 
876 typedef enum {
877   CS_CARTESIAN,
878   CS_POLAR,
879   CS_CYLINDRICAL,
880   CS_SPHERICAL
881 } CoordSystem;
882 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
883 
884 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
885 {
886   PetscInt i;
887 
888   PetscFunctionBegin;
889   if (dim > 3) {
890     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)PetscRealPart(x[i])));
891   } else {
892     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
893 
894     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
895     switch (cs) {
896     case CS_CARTESIAN:
897       for (i = 0; i < dim; ++i) trcoords[i] = coords[i];
898       break;
899     case CS_POLAR:
900       PetscCheck(dim == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
901       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
902       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
903       break;
904     case CS_CYLINDRICAL:
905       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
906       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
907       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
908       trcoords[2] = coords[2];
909       break;
910     case CS_SPHERICAL:
911       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
912       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
913       trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
914       trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
915       break;
916     }
917     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)trcoords[i]));
918   }
919   PetscFunctionReturn(PETSC_SUCCESS);
920 }
921 
922 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
923 {
924   DM_Plex          *mesh = (DM_Plex *)dm->data;
925   DM                cdm, cdmCell;
926   PetscSection      coordSection, coordSectionCell;
927   Vec               coordinates, coordinatesCell;
928   PetscViewerFormat format;
929 
930   PetscFunctionBegin;
931   PetscCall(PetscViewerGetFormat(viewer, &format));
932   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
933     const char *name;
934     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
935     PetscInt    pStart, pEnd, p, numLabels, l;
936     PetscMPIInt rank, size;
937 
938     PetscCall(DMGetCoordinateDM(dm, &cdm));
939     PetscCall(DMGetCoordinateSection(dm, &coordSection));
940     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
941     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
942     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
943     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
944     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
945     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
946     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
947     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
948     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
949     PetscCall(DMGetDimension(dm, &dim));
950     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
951     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
952     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
953     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
954     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
955     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
956     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
957     for (p = pStart; p < pEnd; ++p) {
958       PetscInt dof, off, s;
959 
960       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
961       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
962       for (s = off; s < off + dof; ++s) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
963     }
964     PetscCall(PetscViewerFlush(viewer));
965     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
966     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
967     for (p = pStart; p < pEnd; ++p) {
968       PetscInt dof, off, c;
969 
970       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
971       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
972       for (c = off; c < off + dof; ++c) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " <---- %" PetscInt_FMT " (%" PetscInt_FMT ")\n", rank, p, mesh->cones[c], mesh->coneOrientations[c]));
973     }
974     PetscCall(PetscViewerFlush(viewer));
975     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
976     if (coordSection && coordinates) {
977       CoordSystem        cs = CS_CARTESIAN;
978       const PetscScalar *array, *arrayCell = NULL;
979       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_MAX_INT, pcEnd = PETSC_MIN_INT, pStart, pEnd, p;
980       PetscMPIInt        rank;
981       const char        *name;
982 
983       PetscCall(PetscOptionsGetEnum(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *)&cs, NULL));
984       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
985       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
986       PetscCheck(Nf == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
987       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
988       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
989       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
990       pStart = PetscMin(pvStart, pcStart);
991       pEnd   = PetscMax(pvEnd, pcEnd);
992       PetscCall(PetscObjectGetName((PetscObject)coordinates, &name));
993       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
994       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
995       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
996 
997       PetscCall(VecGetArrayRead(coordinates, &array));
998       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
999       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1000       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
1001       for (p = pStart; p < pEnd; ++p) {
1002         PetscInt dof, off;
1003 
1004         if (p >= pvStart && p < pvEnd) {
1005           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
1006           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
1007           if (dof) {
1008             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1009             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
1010             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1011           }
1012         }
1013         if (cdmCell && p >= pcStart && p < pcEnd) {
1014           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
1015           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
1016           if (dof) {
1017             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1018             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
1019             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1020           }
1021         }
1022       }
1023       PetscCall(PetscViewerFlush(viewer));
1024       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1025       PetscCall(VecRestoreArrayRead(coordinates, &array));
1026       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
1027     }
1028     PetscCall(DMGetNumLabels(dm, &numLabels));
1029     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1030     for (l = 0; l < numLabels; ++l) {
1031       DMLabel     label;
1032       PetscBool   isdepth;
1033       const char *name;
1034 
1035       PetscCall(DMGetLabelName(dm, l, &name));
1036       PetscCall(PetscStrcmp(name, "depth", &isdepth));
1037       if (isdepth) continue;
1038       PetscCall(DMGetLabel(dm, name, &label));
1039       PetscCall(DMLabelView(label, viewer));
1040     }
1041     if (size > 1) {
1042       PetscSF sf;
1043 
1044       PetscCall(DMGetPointSF(dm, &sf));
1045       PetscCall(PetscSFView(sf, viewer));
1046     }
1047     if (mesh->periodic.face_sf) PetscCall(PetscSFView(mesh->periodic.face_sf, viewer));
1048     PetscCall(PetscViewerFlush(viewer));
1049   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
1050     const char  *name, *color;
1051     const char  *defcolors[3]  = {"gray", "orange", "green"};
1052     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
1053     char         lname[PETSC_MAX_PATH_LEN];
1054     PetscReal    scale      = 2.0;
1055     PetscReal    tikzscale  = 1.0;
1056     PetscBool    useNumbers = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
1057     double       tcoords[3];
1058     PetscScalar *coords;
1059     PetscInt     numLabels, l, numColors, numLColors, dim, d, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, e, p, n;
1060     PetscMPIInt  rank, size;
1061     char       **names, **colors, **lcolors;
1062     PetscBool    flg, lflg;
1063     PetscBT      wp = NULL;
1064     PetscInt     pEnd, pStart;
1065 
1066     PetscCall(DMGetCoordinateDM(dm, &cdm));
1067     PetscCall(DMGetCoordinateSection(dm, &coordSection));
1068     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1069     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
1070     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
1071     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
1072     PetscCall(DMGetDimension(dm, &dim));
1073     PetscCall(DMPlexGetDepth(dm, &depth));
1074     PetscCall(DMGetNumLabels(dm, &numLabels));
1075     numLabels  = PetscMax(numLabels, 10);
1076     numColors  = 10;
1077     numLColors = 10;
1078     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
1079     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
1080     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
1081     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
1082     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
1083     for (d = 0; d < 4; ++d) drawColors[d] = PETSC_TRUE;
1084     n = 4;
1085     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
1086     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1087     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
1088     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1089     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
1090     if (!useLabels) numLabels = 0;
1091     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
1092     if (!useColors) {
1093       numColors = 3;
1094       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
1095     }
1096     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
1097     if (!useColors) {
1098       numLColors = 4;
1099       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
1100     }
1101     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
1102     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
1103     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
1104     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
1105     if (depth < dim) plotEdges = PETSC_FALSE;
1106     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
1107 
1108     /* filter points with labelvalue != labeldefaultvalue */
1109     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1110     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1111     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
1112     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1113     if (lflg) {
1114       DMLabel lbl;
1115 
1116       PetscCall(DMGetLabel(dm, lname, &lbl));
1117       if (lbl) {
1118         PetscInt val, defval;
1119 
1120         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
1121         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
1122         for (c = pStart; c < pEnd; c++) {
1123           PetscInt *closure = NULL;
1124           PetscInt  closureSize;
1125 
1126           PetscCall(DMLabelGetValue(lbl, c, &val));
1127           if (val == defval) continue;
1128 
1129           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1130           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
1131           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1132         }
1133       }
1134     }
1135 
1136     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1137     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1138     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1139     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1140 \\documentclass[tikz]{standalone}\n\n\
1141 \\usepackage{pgflibraryshapes}\n\
1142 \\usetikzlibrary{backgrounds}\n\
1143 \\usetikzlibrary{arrows}\n\
1144 \\begin{document}\n"));
1145     if (size > 1) {
1146       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1147       for (p = 0; p < size; ++p) {
1148         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1149         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1150       }
1151       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1152     }
1153     if (drawHasse) {
1154       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, cEnd - cStart));
1155 
1156       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1157       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1158       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1159       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1160       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1161       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1162       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1163       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1164       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1165       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1166       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1167       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1168     }
1169     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1170 
1171     /* Plot vertices */
1172     PetscCall(VecGetArray(coordinates, &coords));
1173     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1174     for (v = vStart; v < vEnd; ++v) {
1175       PetscInt  off, dof, d;
1176       PetscBool isLabeled = PETSC_FALSE;
1177 
1178       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1179       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1180       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1181       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1182       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1183       for (d = 0; d < dof; ++d) {
1184         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1185         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1186       }
1187       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1188       if (dim == 3) {
1189         PetscReal tmp = tcoords[1];
1190         tcoords[1]    = tcoords[2];
1191         tcoords[2]    = -tmp;
1192       }
1193       for (d = 0; d < dof; ++d) {
1194         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1195         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1196       }
1197       if (drawHasse) color = colors[0 % numColors];
1198       else color = colors[rank % numColors];
1199       for (l = 0; l < numLabels; ++l) {
1200         PetscInt val;
1201         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1202         if (val >= 0) {
1203           color     = lcolors[l % numLColors];
1204           isLabeled = PETSC_TRUE;
1205           break;
1206         }
1207       }
1208       if (drawNumbers[0]) {
1209         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1210       } else if (drawColors[0]) {
1211         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1212       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1213     }
1214     PetscCall(VecRestoreArray(coordinates, &coords));
1215     PetscCall(PetscViewerFlush(viewer));
1216     /* Plot edges */
1217     if (plotEdges) {
1218       PetscCall(VecGetArray(coordinates, &coords));
1219       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1220       for (e = eStart; e < eEnd; ++e) {
1221         const PetscInt *cone;
1222         PetscInt        coneSize, offA, offB, dof, d;
1223 
1224         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1225         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1226         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1227         PetscCall(DMPlexGetCone(dm, e, &cone));
1228         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1229         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1230         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1231         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1232         for (d = 0; d < dof; ++d) {
1233           tcoords[d] = (double)(0.5 * scale * PetscRealPart(coords[offA + d] + coords[offB + d]));
1234           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1235         }
1236         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1237         if (dim == 3) {
1238           PetscReal tmp = tcoords[1];
1239           tcoords[1]    = tcoords[2];
1240           tcoords[2]    = -tmp;
1241         }
1242         for (d = 0; d < dof; ++d) {
1243           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1244           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1245         }
1246         if (drawHasse) color = colors[1 % numColors];
1247         else color = colors[rank % numColors];
1248         for (l = 0; l < numLabels; ++l) {
1249           PetscInt val;
1250           PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1251           if (val >= 0) {
1252             color = lcolors[l % numLColors];
1253             break;
1254           }
1255         }
1256         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1257       }
1258       PetscCall(VecRestoreArray(coordinates, &coords));
1259       PetscCall(PetscViewerFlush(viewer));
1260       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1261     }
1262     /* Plot cells */
1263     if (dim == 3 || !drawNumbers[1]) {
1264       for (e = eStart; e < eEnd; ++e) {
1265         const PetscInt *cone;
1266 
1267         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1268         color = colors[rank % numColors];
1269         for (l = 0; l < numLabels; ++l) {
1270           PetscInt val;
1271           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1272           if (val >= 0) {
1273             color = lcolors[l % numLColors];
1274             break;
1275           }
1276         }
1277         PetscCall(DMPlexGetCone(dm, e, &cone));
1278         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1279       }
1280     } else {
1281       DMPolytopeType ct;
1282 
1283       /* Drawing a 2D polygon */
1284       for (c = cStart; c < cEnd; ++c) {
1285         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1286         PetscCall(DMPlexGetCellType(dm, c, &ct));
1287         if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
1288           const PetscInt *cone;
1289           PetscInt        coneSize, e;
1290 
1291           PetscCall(DMPlexGetCone(dm, c, &cone));
1292           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1293           for (e = 0; e < coneSize; ++e) {
1294             const PetscInt *econe;
1295 
1296             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1297             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", colors[rank % numColors], econe[0], rank, cone[e], rank, econe[1], rank));
1298           }
1299         } else {
1300           PetscInt *closure = NULL;
1301           PetscInt  closureSize, Nv = 0, v;
1302 
1303           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1304           for (p = 0; p < closureSize * 2; p += 2) {
1305             const PetscInt point = closure[p];
1306 
1307             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1308           }
1309           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1310           for (v = 0; v <= Nv; ++v) {
1311             const PetscInt vertex = closure[v % Nv];
1312 
1313             if (v > 0) {
1314               if (plotEdges) {
1315                 const PetscInt *edge;
1316                 PetscInt        endpoints[2], ne;
1317 
1318                 endpoints[0] = closure[v - 1];
1319                 endpoints[1] = vertex;
1320                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1321                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1322                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1323                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1324               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1325             }
1326             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1327           }
1328           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1329           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1330         }
1331       }
1332     }
1333     for (c = cStart; c < cEnd; ++c) {
1334       double             ccoords[3] = {0.0, 0.0, 0.0};
1335       PetscBool          isLabeled  = PETSC_FALSE;
1336       PetscScalar       *cellCoords = NULL;
1337       const PetscScalar *array;
1338       PetscInt           numCoords, cdim, d;
1339       PetscBool          isDG;
1340 
1341       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1342       PetscCall(DMGetCoordinateDim(dm, &cdim));
1343       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1344       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1345       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1346       for (p = 0; p < numCoords / cdim; ++p) {
1347         for (d = 0; d < cdim; ++d) {
1348           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1349           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1350         }
1351         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1352         if (cdim == 3) {
1353           PetscReal tmp = tcoords[1];
1354           tcoords[1]    = tcoords[2];
1355           tcoords[2]    = -tmp;
1356         }
1357         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1358       }
1359       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1360       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1361       for (d = 0; d < cdim; ++d) {
1362         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1363         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)ccoords[d]));
1364       }
1365       if (drawHasse) color = colors[depth % numColors];
1366       else color = colors[rank % numColors];
1367       for (l = 0; l < numLabels; ++l) {
1368         PetscInt val;
1369         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1370         if (val >= 0) {
1371           color     = lcolors[l % numLColors];
1372           isLabeled = PETSC_TRUE;
1373           break;
1374         }
1375       }
1376       if (drawNumbers[dim]) {
1377         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1378       } else if (drawColors[dim]) {
1379         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1380       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1381     }
1382     if (drawHasse) {
1383       color = colors[depth % numColors];
1384       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1385       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1386       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1387       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,0) {\\c};\n", rank, color));
1388       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1389 
1390       color = colors[1 % numColors];
1391       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1392       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1393       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1394       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,1) {\\e};\n", rank, color));
1395       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1396 
1397       color = colors[0 % numColors];
1398       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1399       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1400       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1401       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,2) {\\v};\n", rank, color));
1402       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1403 
1404       for (p = pStart; p < pEnd; ++p) {
1405         const PetscInt *cone;
1406         PetscInt        coneSize, cp;
1407 
1408         PetscCall(DMPlexGetCone(dm, p, &cone));
1409         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1410         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1411       }
1412     }
1413     PetscCall(PetscViewerFlush(viewer));
1414     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1415     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1416     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1417     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1418     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1419     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1420     PetscCall(PetscFree3(names, colors, lcolors));
1421     PetscCall(PetscBTDestroy(&wp));
1422   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1423     Vec                    cown, acown;
1424     VecScatter             sct;
1425     ISLocalToGlobalMapping g2l;
1426     IS                     gid, acis;
1427     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1428     MPI_Group              ggroup, ngroup;
1429     PetscScalar           *array, nid;
1430     const PetscInt        *idxs;
1431     PetscInt              *idxs2, *start, *adjacency, *work;
1432     PetscInt64             lm[3], gm[3];
1433     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1434     PetscMPIInt            d1, d2, rank;
1435 
1436     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1437     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1438 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1439     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1440 #endif
1441     if (ncomm != MPI_COMM_NULL) {
1442       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1443       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1444       d1 = 0;
1445       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1446       nid = d2;
1447       PetscCallMPI(MPI_Group_free(&ggroup));
1448       PetscCallMPI(MPI_Group_free(&ngroup));
1449       PetscCallMPI(MPI_Comm_free(&ncomm));
1450     } else nid = 0.0;
1451 
1452     /* Get connectivity */
1453     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1454     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1455 
1456     /* filter overlapped local cells */
1457     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1458     PetscCall(ISGetIndices(gid, &idxs));
1459     PetscCall(ISGetLocalSize(gid, &cum));
1460     PetscCall(PetscMalloc1(cum, &idxs2));
1461     for (c = cStart, cum = 0; c < cEnd; c++) {
1462       if (idxs[c - cStart] < 0) continue;
1463       idxs2[cum++] = idxs[c - cStart];
1464     }
1465     PetscCall(ISRestoreIndices(gid, &idxs));
1466     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1467     PetscCall(ISDestroy(&gid));
1468     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1469 
1470     /* support for node-aware cell locality */
1471     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1472     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1473     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1474     PetscCall(VecGetArray(cown, &array));
1475     for (c = 0; c < numVertices; c++) array[c] = nid;
1476     PetscCall(VecRestoreArray(cown, &array));
1477     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1478     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1479     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1480     PetscCall(ISDestroy(&acis));
1481     PetscCall(VecScatterDestroy(&sct));
1482     PetscCall(VecDestroy(&cown));
1483 
1484     /* compute edgeCut */
1485     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1486     PetscCall(PetscMalloc1(cum, &work));
1487     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1488     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1489     PetscCall(ISDestroy(&gid));
1490     PetscCall(VecGetArray(acown, &array));
1491     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1492       PetscInt totl;
1493 
1494       totl = start[c + 1] - start[c];
1495       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1496       for (i = 0; i < totl; i++) {
1497         if (work[i] < 0) {
1498           ect += 1;
1499           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1500         }
1501       }
1502     }
1503     PetscCall(PetscFree(work));
1504     PetscCall(VecRestoreArray(acown, &array));
1505     lm[0] = numVertices > 0 ? numVertices : PETSC_MAX_INT;
1506     lm[1] = -numVertices;
1507     PetscCall(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1508     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT, -((double)gm[1]) / ((double)gm[0]), -(PetscInt)gm[1], (PetscInt)gm[0]));
1509     lm[0] = ect;                     /* edgeCut */
1510     lm[1] = ectn;                    /* node-aware edgeCut */
1511     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1512     PetscCall(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1513     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt_FMT ")\n", (PetscInt)gm[2]));
1514 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1515     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), gm[0] ? ((double)(gm[1])) / ((double)gm[0]) : 1.));
1516 #else
1517     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), 0.0));
1518 #endif
1519     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1520     PetscCall(PetscFree(start));
1521     PetscCall(PetscFree(adjacency));
1522     PetscCall(VecDestroy(&acown));
1523   } else {
1524     const char    *name;
1525     PetscInt      *sizes, *hybsizes, *ghostsizes;
1526     PetscInt       locDepth, depth, cellHeight, dim, d;
1527     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1528     PetscInt       numLabels, l, maxSize = 17;
1529     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1530     MPI_Comm       comm;
1531     PetscMPIInt    size, rank;
1532 
1533     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1534     PetscCallMPI(MPI_Comm_size(comm, &size));
1535     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1536     PetscCall(DMGetDimension(dm, &dim));
1537     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1538     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1539     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1540     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1541     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1542     PetscCall(DMPlexGetDepth(dm, &locDepth));
1543     PetscCall(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1544     PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &gcStart, &gcEnd));
1545     gcNum = gcEnd - gcStart;
1546     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1547     else PetscCall(PetscCalloc3(3, &sizes, 3, &hybsizes, 3, &ghostsizes));
1548     for (d = 0; d <= depth; d++) {
1549       PetscInt Nc[2] = {0, 0}, ict;
1550 
1551       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1552       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1553       ict = ct0;
1554       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1555       ct0 = (DMPolytopeType)ict;
1556       for (p = pStart; p < pEnd; ++p) {
1557         DMPolytopeType ct;
1558 
1559         PetscCall(DMPlexGetCellType(dm, p, &ct));
1560         if (ct == ct0) ++Nc[0];
1561         else ++Nc[1];
1562       }
1563       if (size < maxSize) {
1564         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes, 1, MPIU_INT, 0, comm));
1565         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1566         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1567         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1568         for (p = 0; p < size; ++p) {
1569           if (rank == 0) {
1570             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p] + hybsizes[p]));
1571             if (hybsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1572             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1573           }
1574         }
1575       } else {
1576         PetscInt locMinMax[2];
1577 
1578         locMinMax[0] = Nc[0] + Nc[1];
1579         locMinMax[1] = Nc[0] + Nc[1];
1580         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1581         locMinMax[0] = Nc[1];
1582         locMinMax[1] = Nc[1];
1583         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1584         if (d == depth) {
1585           locMinMax[0] = gcNum;
1586           locMinMax[1] = gcNum;
1587           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1588         }
1589         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1590         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1591         if (hybsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1592         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1593       }
1594       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1595     }
1596     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1597     {
1598       const PetscReal *maxCell;
1599       const PetscReal *L;
1600       PetscBool        localized;
1601 
1602       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1603       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1604       if (L || localized) {
1605         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1606         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1607         if (L) {
1608           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1609           for (d = 0; d < dim; ++d) {
1610             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1611             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1612           }
1613           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1614         }
1615         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1616         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1617       }
1618     }
1619     PetscCall(DMGetNumLabels(dm, &numLabels));
1620     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1621     for (l = 0; l < numLabels; ++l) {
1622       DMLabel         label;
1623       const char     *name;
1624       IS              valueIS;
1625       const PetscInt *values;
1626       PetscInt        numValues, v;
1627 
1628       PetscCall(DMGetLabelName(dm, l, &name));
1629       PetscCall(DMGetLabel(dm, name, &label));
1630       PetscCall(DMLabelGetNumValues(label, &numValues));
1631       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1632       PetscCall(DMLabelGetValueIS(label, &valueIS));
1633       PetscCall(ISGetIndices(valueIS, &values));
1634       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1635       for (v = 0; v < numValues; ++v) {
1636         PetscInt size;
1637 
1638         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1639         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1640         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1641       }
1642       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1643       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1644       PetscCall(ISRestoreIndices(valueIS, &values));
1645       PetscCall(ISDestroy(&valueIS));
1646     }
1647     {
1648       char    **labelNames;
1649       PetscInt  Nl = numLabels;
1650       PetscBool flg;
1651 
1652       PetscCall(PetscMalloc1(Nl, &labelNames));
1653       PetscCall(PetscOptionsGetStringArray(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1654       for (l = 0; l < Nl; ++l) {
1655         DMLabel label;
1656 
1657         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1658         if (flg) {
1659           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1660           PetscCall(DMLabelView(label, viewer));
1661         }
1662         PetscCall(PetscFree(labelNames[l]));
1663       }
1664       PetscCall(PetscFree(labelNames));
1665     }
1666     /* If no fields are specified, people do not want to see adjacency */
1667     if (dm->Nf) {
1668       PetscInt f;
1669 
1670       for (f = 0; f < dm->Nf; ++f) {
1671         const char *name;
1672 
1673         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1674         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1675         PetscCall(PetscViewerASCIIPushTab(viewer));
1676         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1677         if (dm->fields[f].adjacency[0]) {
1678           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1679           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1680         } else {
1681           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1682           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1683         }
1684         PetscCall(PetscViewerASCIIPopTab(viewer));
1685       }
1686     }
1687     PetscCall(DMGetCoarseDM(dm, &cdm));
1688     if (cdm) {
1689       PetscCall(PetscViewerASCIIPushTab(viewer));
1690       PetscCall(PetscViewerASCIIPrintf(viewer, "Defined by transform from:\n"));
1691       PetscCall(DMPlexView_Ascii(cdm, viewer));
1692       PetscCall(PetscViewerASCIIPopTab(viewer));
1693     }
1694   }
1695   PetscFunctionReturn(PETSC_SUCCESS);
1696 }
1697 
1698 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1699 {
1700   DMPolytopeType ct;
1701   PetscMPIInt    rank;
1702   PetscInt       cdim;
1703 
1704   PetscFunctionBegin;
1705   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1706   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1707   PetscCall(DMGetCoordinateDim(dm, &cdim));
1708   switch (ct) {
1709   case DM_POLYTOPE_SEGMENT:
1710   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1711     switch (cdim) {
1712     case 1: {
1713       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1714       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1715 
1716       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, PETSC_DRAW_BLACK));
1717       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, PETSC_DRAW_BLACK));
1718       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, PETSC_DRAW_BLACK));
1719     } break;
1720     case 2: {
1721       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1722       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1723       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1724 
1725       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1726       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]) + l * dx, PetscRealPart(coords[1]) + l * dy, PetscRealPart(coords[0]) - l * dx, PetscRealPart(coords[1]) - l * dy, PETSC_DRAW_BLACK));
1727       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]) + l * dx, PetscRealPart(coords[3]) + l * dy, PetscRealPart(coords[2]) - l * dx, PetscRealPart(coords[3]) - l * dy, PETSC_DRAW_BLACK));
1728     } break;
1729     default:
1730       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1731     }
1732     break;
1733   case DM_POLYTOPE_TRIANGLE:
1734     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1735     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1736     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1737     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1738     break;
1739   case DM_POLYTOPE_QUADRILATERAL:
1740     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1741     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1742     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1743     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1744     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1745     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1746     break;
1747   case DM_POLYTOPE_SEG_PRISM_TENSOR:
1748     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1749     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1750     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1751     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1752     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1753     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1754     break;
1755   case DM_POLYTOPE_FV_GHOST:
1756     break;
1757   default:
1758     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1759   }
1760   PetscFunctionReturn(PETSC_SUCCESS);
1761 }
1762 
1763 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1764 {
1765   DMPolytopeType ct;
1766   PetscReal      centroid[2] = {0., 0.};
1767   PetscMPIInt    rank;
1768   PetscInt       fillColor, v, e, d;
1769 
1770   PetscFunctionBegin;
1771   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1772   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1773   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1774   switch (ct) {
1775   case DM_POLYTOPE_TRIANGLE: {
1776     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1777 
1778     for (v = 0; v < 3; ++v) {
1779       centroid[0] += PetscRealPart(coords[v * 2 + 0]) / 3.;
1780       centroid[1] += PetscRealPart(coords[v * 2 + 1]) / 3.;
1781     }
1782     for (e = 0; e < 3; ++e) {
1783       refCoords[0] = refVertices[e * 2 + 0];
1784       refCoords[1] = refVertices[e * 2 + 1];
1785       for (d = 1; d <= edgeDiv; ++d) {
1786         refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % 3 * 2 + 0] - refCoords[0]) * d / edgeDiv;
1787         refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % 3 * 2 + 1] - refCoords[1]) * d / edgeDiv;
1788       }
1789       PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1790       for (d = 0; d < edgeDiv; ++d) {
1791         PetscCall(PetscDrawTriangle(draw, centroid[0], centroid[1], edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], fillColor, fillColor, fillColor));
1792         PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1793       }
1794     }
1795   } break;
1796   default:
1797     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1798   }
1799   PetscFunctionReturn(PETSC_SUCCESS);
1800 }
1801 
1802 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1803 {
1804   PetscDraw    draw;
1805   DM           cdm;
1806   PetscSection coordSection;
1807   Vec          coordinates;
1808   PetscReal    xyl[3], xyr[3];
1809   PetscReal   *refCoords, *edgeCoords;
1810   PetscBool    isnull, drawAffine = PETSC_TRUE;
1811   PetscInt     dim, vStart, vEnd, cStart, cEnd, c, edgeDiv = 4;
1812 
1813   PetscFunctionBegin;
1814   PetscCall(DMGetCoordinateDim(dm, &dim));
1815   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1816   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1817   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1818   PetscCall(DMGetCoordinateDM(dm, &cdm));
1819   PetscCall(DMGetLocalSection(cdm, &coordSection));
1820   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1821   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1822   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1823 
1824   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1825   PetscCall(PetscDrawIsNull(draw, &isnull));
1826   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
1827   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1828 
1829   PetscCall(DMGetBoundingBox(dm, xyl, xyr));
1830   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1831   PetscCall(PetscDrawClear(draw));
1832 
1833   for (c = cStart; c < cEnd; ++c) {
1834     PetscScalar       *coords = NULL;
1835     const PetscScalar *coords_arr;
1836     PetscInt           numCoords;
1837     PetscBool          isDG;
1838 
1839     PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1840     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1841     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1842     PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1843   }
1844   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1845   PetscCall(PetscDrawFlush(draw));
1846   PetscCall(PetscDrawPause(draw));
1847   PetscCall(PetscDrawSave(draw));
1848   PetscFunctionReturn(PETSC_SUCCESS);
1849 }
1850 
1851 static PetscErrorCode DMPlexCreateHighOrderSurrogate_Internal(DM dm, DM *hdm)
1852 {
1853   DM           odm = dm, rdm = dm, cdm;
1854   PetscFE      fe;
1855   PetscSpace   sp;
1856   PetscClassId id;
1857   PetscInt     degree;
1858   PetscBool    hoView = PETSC_TRUE;
1859 
1860   PetscFunctionBegin;
1861   PetscObjectOptionsBegin((PetscObject)dm);
1862   PetscCall(PetscOptionsBool("-dm_plex_high_order_view", "Subsample to view meshes with high order coordinates", "DMPlexCreateHighOrderSurrogate_Internal", hoView, &hoView, NULL));
1863   PetscOptionsEnd();
1864   PetscCall(PetscObjectReference((PetscObject)dm));
1865   *hdm = dm;
1866   if (!hoView) PetscFunctionReturn(PETSC_SUCCESS);
1867   PetscCall(DMGetCoordinateDM(dm, &cdm));
1868   PetscCall(DMGetField(cdm, 0, NULL, (PetscObject *)&fe));
1869   PetscCall(PetscObjectGetClassId((PetscObject)fe, &id));
1870   if (id != PETSCFE_CLASSID) PetscFunctionReturn(PETSC_SUCCESS);
1871   PetscCall(PetscFEGetBasisSpace(fe, &sp));
1872   PetscCall(PetscSpaceGetDegree(sp, &degree, NULL));
1873   for (PetscInt r = 0, rd = PetscCeilReal(((PetscReal)degree) / 2.); r < (PetscInt)PetscCeilReal(PetscLog2Real(degree)); ++r, rd = PetscCeilReal(((PetscReal)rd) / 2.)) {
1874     DM  cdm, rcdm;
1875     Mat In;
1876     Vec cl, rcl;
1877 
1878     PetscCall(DMRefine(odm, PetscObjectComm((PetscObject)odm), &rdm));
1879     if (rd > 1) PetscCall(DMPlexCreateCoordinateSpace(rdm, rd, PETSC_FALSE, NULL));
1880     PetscCall(PetscObjectSetName((PetscObject)rdm, "Refined Mesh with Linear Coordinates"));
1881     PetscCall(DMGetCoordinateDM(odm, &cdm));
1882     PetscCall(DMGetCoordinateDM(rdm, &rcdm));
1883     PetscCall(DMGetCoordinatesLocal(odm, &cl));
1884     PetscCall(DMGetCoordinatesLocal(rdm, &rcl));
1885     PetscCall(DMSetCoarseDM(rcdm, cdm));
1886     PetscCall(DMCreateInterpolation(cdm, rcdm, &In, NULL));
1887     PetscCall(MatMult(In, cl, rcl));
1888     PetscCall(MatDestroy(&In));
1889     PetscCall(DMSetCoordinatesLocal(rdm, rcl));
1890     PetscCall(DMDestroy(&odm));
1891     odm = rdm;
1892   }
1893   *hdm = rdm;
1894   PetscFunctionReturn(PETSC_SUCCESS);
1895 }
1896 
1897 #if defined(PETSC_HAVE_EXODUSII)
1898   #include <exodusII.h>
1899   #include <petscviewerexodusii.h>
1900 #endif
1901 
1902 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1903 {
1904   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns;
1905   char      name[PETSC_MAX_PATH_LEN];
1906 
1907   PetscFunctionBegin;
1908   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1909   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1910   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
1911   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
1912   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1913   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
1914   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
1915   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
1916   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
1917   if (iascii) {
1918     PetscViewerFormat format;
1919     PetscCall(PetscViewerGetFormat(viewer, &format));
1920     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1921     else PetscCall(DMPlexView_Ascii(dm, viewer));
1922   } else if (ishdf5) {
1923 #if defined(PETSC_HAVE_HDF5)
1924     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1925 #else
1926     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1927 #endif
1928   } else if (isvtk) {
1929     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
1930   } else if (isdraw) {
1931     DM hdm;
1932 
1933     PetscCall(DMPlexCreateHighOrderSurrogate_Internal(dm, &hdm));
1934     PetscCall(DMPlexView_Draw(hdm, viewer));
1935     PetscCall(DMDestroy(&hdm));
1936   } else if (isglvis) {
1937     PetscCall(DMPlexView_GLVis(dm, viewer));
1938 #if defined(PETSC_HAVE_EXODUSII)
1939   } else if (isexodus) {
1940     /*
1941       exodusII requires that all sets be part of exactly one cell set.
1942       If the dm does not have a "Cell Sets" label defined, we create one
1943       with ID 1, containing all cells.
1944       Note that if the Cell Sets label is defined but does not cover all cells,
1945       we may still have a problem. This should probably be checked here or in the viewer;
1946     */
1947     PetscInt numCS;
1948     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
1949     if (!numCS) {
1950       PetscInt cStart, cEnd, c;
1951       PetscCall(DMCreateLabel(dm, "Cell Sets"));
1952       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1953       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
1954     }
1955     PetscCall(DMView_PlexExodusII(dm, viewer));
1956 #endif
1957 #if defined(PETSC_HAVE_CGNS)
1958   } else if (iscgns) {
1959     PetscCall(DMView_PlexCGNS(dm, viewer));
1960 #endif
1961   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
1962   /* Optionally view the partition */
1963   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
1964   if (flg) {
1965     Vec ranks;
1966     PetscCall(DMPlexCreateRankField(dm, &ranks));
1967     PetscCall(VecView(ranks, viewer));
1968     PetscCall(VecDestroy(&ranks));
1969   }
1970   /* Optionally view a label */
1971   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
1972   if (flg) {
1973     DMLabel label;
1974     Vec     val;
1975 
1976     PetscCall(DMGetLabel(dm, name, &label));
1977     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
1978     PetscCall(DMPlexCreateLabelField(dm, label, &val));
1979     PetscCall(VecView(val, viewer));
1980     PetscCall(VecDestroy(&val));
1981   }
1982   PetscFunctionReturn(PETSC_SUCCESS);
1983 }
1984 
1985 /*@
1986   DMPlexTopologyView - Saves a `DMPLEX` topology into a file
1987 
1988   Collective
1989 
1990   Input Parameters:
1991 + dm     - The `DM` whose topology is to be saved
1992 - viewer - The `PetscViewer` to save it in
1993 
1994   Level: advanced
1995 
1996 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`, `PetscViewer`
1997 @*/
1998 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
1999 {
2000   PetscBool ishdf5;
2001 
2002   PetscFunctionBegin;
2003   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2004   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2005   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2006   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
2007   if (ishdf5) {
2008 #if defined(PETSC_HAVE_HDF5)
2009     PetscViewerFormat format;
2010     PetscCall(PetscViewerGetFormat(viewer, &format));
2011     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2012       IS globalPointNumbering;
2013 
2014       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2015       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
2016       PetscCall(ISDestroy(&globalPointNumbering));
2017     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2018 #else
2019     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2020 #endif
2021   }
2022   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
2023   PetscFunctionReturn(PETSC_SUCCESS);
2024 }
2025 
2026 /*@
2027   DMPlexCoordinatesView - Saves `DMPLEX` coordinates into a file
2028 
2029   Collective
2030 
2031   Input Parameters:
2032 + dm     - The `DM` whose coordinates are to be saved
2033 - viewer - The `PetscViewer` for saving
2034 
2035   Level: advanced
2036 
2037 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`, `PetscViewer`
2038 @*/
2039 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
2040 {
2041   PetscBool ishdf5;
2042 
2043   PetscFunctionBegin;
2044   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2045   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2046   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2047   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2048   if (ishdf5) {
2049 #if defined(PETSC_HAVE_HDF5)
2050     PetscViewerFormat format;
2051     PetscCall(PetscViewerGetFormat(viewer, &format));
2052     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2053       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
2054     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2055 #else
2056     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2057 #endif
2058   }
2059   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2060   PetscFunctionReturn(PETSC_SUCCESS);
2061 }
2062 
2063 /*@
2064   DMPlexLabelsView - Saves `DMPLEX` labels into a file
2065 
2066   Collective
2067 
2068   Input Parameters:
2069 + dm     - The `DM` whose labels are to be saved
2070 - viewer - The `PetscViewer` for saving
2071 
2072   Level: advanced
2073 
2074 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`, `PetscViewer`
2075 @*/
2076 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
2077 {
2078   PetscBool ishdf5;
2079 
2080   PetscFunctionBegin;
2081   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2082   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2083   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2084   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
2085   if (ishdf5) {
2086 #if defined(PETSC_HAVE_HDF5)
2087     IS                globalPointNumbering;
2088     PetscViewerFormat format;
2089 
2090     PetscCall(PetscViewerGetFormat(viewer, &format));
2091     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2092       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2093       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
2094       PetscCall(ISDestroy(&globalPointNumbering));
2095     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2096 #else
2097     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2098 #endif
2099   }
2100   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
2101   PetscFunctionReturn(PETSC_SUCCESS);
2102 }
2103 
2104 /*@
2105   DMPlexSectionView - Saves a section associated with a `DMPLEX`
2106 
2107   Collective
2108 
2109   Input Parameters:
2110 + dm        - The `DM` that contains the topology on which the section to be saved is defined
2111 . viewer    - The `PetscViewer` for saving
2112 - sectiondm - The `DM` that contains the section to be saved
2113 
2114   Level: advanced
2115 
2116   Notes:
2117   This function is a wrapper around `PetscSectionView()`; in addition to the raw section, it saves information that associates the section points to the topology (`dm`) points. When the topology (`dm`) and the section are later loaded with `DMPlexTopologyLoad()` and `DMPlexSectionLoad()`, respectively, this information is used to match section points with topology points.
2118 
2119   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2120 
2121 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`, `PetscViewer`
2122 @*/
2123 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
2124 {
2125   PetscBool ishdf5;
2126 
2127   PetscFunctionBegin;
2128   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2129   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2130   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2131   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2132   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
2133   if (ishdf5) {
2134 #if defined(PETSC_HAVE_HDF5)
2135     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
2136 #else
2137     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2138 #endif
2139   }
2140   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
2141   PetscFunctionReturn(PETSC_SUCCESS);
2142 }
2143 
2144 /*@
2145   DMPlexGlobalVectorView - Saves a global vector
2146 
2147   Collective
2148 
2149   Input Parameters:
2150 + dm        - The `DM` that represents the topology
2151 . viewer    - The `PetscViewer` to save data with
2152 . sectiondm - The `DM` that contains the global section on which vec is defined
2153 - vec       - The global vector to be saved
2154 
2155   Level: advanced
2156 
2157   Notes:
2158   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2159 
2160   Calling sequence:
2161 .vb
2162        DMCreate(PETSC_COMM_WORLD, &dm);
2163        DMSetType(dm, DMPLEX);
2164        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2165        DMClone(dm, &sectiondm);
2166        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2167        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2168        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2169        PetscSectionSetChart(section, pStart, pEnd);
2170        PetscSectionSetUp(section);
2171        DMSetLocalSection(sectiondm, section);
2172        PetscSectionDestroy(&section);
2173        DMGetGlobalVector(sectiondm, &vec);
2174        PetscObjectSetName((PetscObject)vec, "vec_name");
2175        DMPlexTopologyView(dm, viewer);
2176        DMPlexSectionView(dm, viewer, sectiondm);
2177        DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
2178        DMRestoreGlobalVector(sectiondm, &vec);
2179        DMDestroy(&sectiondm);
2180        DMDestroy(&dm);
2181 .ve
2182 
2183 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2184 @*/
2185 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2186 {
2187   PetscBool ishdf5;
2188 
2189   PetscFunctionBegin;
2190   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2191   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2192   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2193   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2194   /* Check consistency */
2195   {
2196     PetscSection section;
2197     PetscBool    includesConstraints;
2198     PetscInt     m, m1;
2199 
2200     PetscCall(VecGetLocalSize(vec, &m1));
2201     PetscCall(DMGetGlobalSection(sectiondm, &section));
2202     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2203     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2204     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2205     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2206   }
2207   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2208   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2209   if (ishdf5) {
2210 #if defined(PETSC_HAVE_HDF5)
2211     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2212 #else
2213     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2214 #endif
2215   }
2216   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2217   PetscFunctionReturn(PETSC_SUCCESS);
2218 }
2219 
2220 /*@
2221   DMPlexLocalVectorView - Saves a local vector
2222 
2223   Collective
2224 
2225   Input Parameters:
2226 + dm        - The `DM` that represents the topology
2227 . viewer    - The `PetscViewer` to save data with
2228 . sectiondm - The `DM` that contains the local section on which `vec` is defined; may be the same as `dm`
2229 - vec       - The local vector to be saved
2230 
2231   Level: advanced
2232 
2233   Note:
2234   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2235 
2236   Calling sequence:
2237 .vb
2238        DMCreate(PETSC_COMM_WORLD, &dm);
2239        DMSetType(dm, DMPLEX);
2240        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2241        DMClone(dm, &sectiondm);
2242        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2243        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2244        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2245        PetscSectionSetChart(section, pStart, pEnd);
2246        PetscSectionSetUp(section);
2247        DMSetLocalSection(sectiondm, section);
2248        DMGetLocalVector(sectiondm, &vec);
2249        PetscObjectSetName((PetscObject)vec, "vec_name");
2250        DMPlexTopologyView(dm, viewer);
2251        DMPlexSectionView(dm, viewer, sectiondm);
2252        DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2253        DMRestoreLocalVector(sectiondm, &vec);
2254        DMDestroy(&sectiondm);
2255        DMDestroy(&dm);
2256 .ve
2257 
2258 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2259 @*/
2260 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2261 {
2262   PetscBool ishdf5;
2263 
2264   PetscFunctionBegin;
2265   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2266   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2267   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2268   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2269   /* Check consistency */
2270   {
2271     PetscSection section;
2272     PetscBool    includesConstraints;
2273     PetscInt     m, m1;
2274 
2275     PetscCall(VecGetLocalSize(vec, &m1));
2276     PetscCall(DMGetLocalSection(sectiondm, &section));
2277     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2278     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2279     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2280     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2281   }
2282   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2283   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2284   if (ishdf5) {
2285 #if defined(PETSC_HAVE_HDF5)
2286     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2287 #else
2288     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2289 #endif
2290   }
2291   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2292   PetscFunctionReturn(PETSC_SUCCESS);
2293 }
2294 
2295 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2296 {
2297   PetscBool ishdf5;
2298 
2299   PetscFunctionBegin;
2300   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2301   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2302   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2303   if (ishdf5) {
2304 #if defined(PETSC_HAVE_HDF5)
2305     PetscViewerFormat format;
2306     PetscCall(PetscViewerGetFormat(viewer, &format));
2307     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2308       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2309     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2310       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2311     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2312     PetscFunctionReturn(PETSC_SUCCESS);
2313 #else
2314     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2315 #endif
2316   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2317 }
2318 
2319 /*@
2320   DMPlexTopologyLoad - Loads a topology into a `DMPLEX`
2321 
2322   Collective
2323 
2324   Input Parameters:
2325 + dm     - The `DM` into which the topology is loaded
2326 - viewer - The `PetscViewer` for the saved topology
2327 
2328   Output Parameter:
2329 . globalToLocalPointSF - The `PetscSF` that pushes points in [0, N) to the associated points in the loaded `DMPLEX`, where N is the global number of points; `NULL` if unneeded
2330 
2331   Level: advanced
2332 
2333 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2334           `PetscViewer`, `PetscSF`
2335 @*/
2336 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2337 {
2338   PetscBool ishdf5;
2339 
2340   PetscFunctionBegin;
2341   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2342   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2343   if (globalToLocalPointSF) PetscAssertPointer(globalToLocalPointSF, 3);
2344   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2345   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2346   if (ishdf5) {
2347 #if defined(PETSC_HAVE_HDF5)
2348     PetscViewerFormat format;
2349     PetscCall(PetscViewerGetFormat(viewer, &format));
2350     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2351       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2352     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2353 #else
2354     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2355 #endif
2356   }
2357   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2358   PetscFunctionReturn(PETSC_SUCCESS);
2359 }
2360 
2361 /*@
2362   DMPlexCoordinatesLoad - Loads coordinates into a `DMPLEX`
2363 
2364   Collective
2365 
2366   Input Parameters:
2367 + dm                   - The `DM` into which the coordinates are loaded
2368 . viewer               - The `PetscViewer` for the saved coordinates
2369 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2370 
2371   Level: advanced
2372 
2373 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2374           `PetscSF`, `PetscViewer`
2375 @*/
2376 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2377 {
2378   PetscBool ishdf5;
2379 
2380   PetscFunctionBegin;
2381   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2382   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2383   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2384   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2385   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2386   if (ishdf5) {
2387 #if defined(PETSC_HAVE_HDF5)
2388     PetscViewerFormat format;
2389     PetscCall(PetscViewerGetFormat(viewer, &format));
2390     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2391       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2392     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2393 #else
2394     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2395 #endif
2396   }
2397   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2398   PetscFunctionReturn(PETSC_SUCCESS);
2399 }
2400 
2401 /*@
2402   DMPlexLabelsLoad - Loads labels into a `DMPLEX`
2403 
2404   Collective
2405 
2406   Input Parameters:
2407 + dm                   - The `DM` into which the labels are loaded
2408 . viewer               - The `PetscViewer` for the saved labels
2409 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading `dm` from viewer
2410 
2411   Level: advanced
2412 
2413   Note:
2414   The `PetscSF` argument must not be NULL if the `DM` is distributed, otherwise an error occurs.
2415 
2416 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2417           `PetscSF`, `PetscViewer`
2418 @*/
2419 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2420 {
2421   PetscBool ishdf5;
2422 
2423   PetscFunctionBegin;
2424   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2425   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2426   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2427   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2428   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2429   if (ishdf5) {
2430 #if defined(PETSC_HAVE_HDF5)
2431     PetscViewerFormat format;
2432 
2433     PetscCall(PetscViewerGetFormat(viewer, &format));
2434     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2435       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2436     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2437 #else
2438     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2439 #endif
2440   }
2441   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2442   PetscFunctionReturn(PETSC_SUCCESS);
2443 }
2444 
2445 /*@
2446   DMPlexSectionLoad - Loads section into a `DMPLEX`
2447 
2448   Collective
2449 
2450   Input Parameters:
2451 + dm                   - The `DM` that represents the topology
2452 . viewer               - The `PetscViewer` that represents the on-disk section (sectionA)
2453 . sectiondm            - The `DM` into which the on-disk section (sectionA) is migrated
2454 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad(`) when loading dm from viewer
2455 
2456   Output Parameters:
2457 + globalDofSF - The `PetscSF` that migrates any on-disk `Vec` data associated with sectionA into a global `Vec` associated with the `sectiondm`'s global section (`NULL` if not needed)
2458 - localDofSF  - The `PetscSF` that migrates any on-disk `Vec` data associated with sectionA into a local `Vec` associated with the `sectiondm`'s local section (`NULL` if not needed)
2459 
2460   Level: advanced
2461 
2462   Notes:
2463   This function is a wrapper around `PetscSectionLoad()`; it loads, in addition to the raw section, a list of global point numbers that associates each on-disk section point with a global point number in [0, NX), where NX is the number of topology points in `dm`. Noting that globalToLocalPointSF associates each topology point in dm with a global number in [0, NX), one can readily establish an association of the on-disk section points with the topology points.
2464 
2465   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2466 
2467   The output parameter, `globalDofSF` (`localDofSF`), can later be used with `DMPlexGlobalVectorLoad()` (`DMPlexLocalVectorLoad()`) to load on-disk vectors into global (local) vectors associated with sectiondm's global (local) section.
2468 
2469   Example using 2 processes:
2470 .vb
2471   NX (number of points on dm): 4
2472   sectionA                   : the on-disk section
2473   vecA                       : a vector associated with sectionA
2474   sectionB                   : sectiondm's local section constructed in this function
2475   vecB (local)               : a vector associated with sectiondm's local section
2476   vecB (global)              : a vector associated with sectiondm's global section
2477 
2478                                      rank 0    rank 1
2479   vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2480   sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2481   sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2482   sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2483   [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2484   sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2485   sectionB->atlasDof             :     1 0 1 | 1 3
2486   sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2487   vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2488   vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2489 .ve
2490   where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2491 
2492 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`, `PetscSF`, `PetscViewer`
2493 @*/
2494 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
2495 {
2496   PetscBool ishdf5;
2497 
2498   PetscFunctionBegin;
2499   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2500   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2501   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2502   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2503   if (globalDofSF) PetscAssertPointer(globalDofSF, 5);
2504   if (localDofSF) PetscAssertPointer(localDofSF, 6);
2505   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2506   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2507   if (ishdf5) {
2508 #if defined(PETSC_HAVE_HDF5)
2509     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2510 #else
2511     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2512 #endif
2513   }
2514   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2515   PetscFunctionReturn(PETSC_SUCCESS);
2516 }
2517 
2518 /*@
2519   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2520 
2521   Collective
2522 
2523   Input Parameters:
2524 + dm        - The `DM` that represents the topology
2525 . viewer    - The `PetscViewer` that represents the on-disk vector data
2526 . sectiondm - The `DM` that contains the global section on which vec is defined
2527 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2528 - vec       - The global vector to set values of
2529 
2530   Level: advanced
2531 
2532   Notes:
2533   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2534 
2535   Calling sequence:
2536 .vb
2537        DMCreate(PETSC_COMM_WORLD, &dm);
2538        DMSetType(dm, DMPLEX);
2539        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2540        DMPlexTopologyLoad(dm, viewer, &sfX);
2541        DMClone(dm, &sectiondm);
2542        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2543        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2544        DMGetGlobalVector(sectiondm, &vec);
2545        PetscObjectSetName((PetscObject)vec, "vec_name");
2546        DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2547        DMRestoreGlobalVector(sectiondm, &vec);
2548        PetscSFDestroy(&gsf);
2549        PetscSFDestroy(&sfX);
2550        DMDestroy(&sectiondm);
2551        DMDestroy(&dm);
2552 .ve
2553 
2554 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2555           `PetscSF`, `PetscViewer`
2556 @*/
2557 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2558 {
2559   PetscBool ishdf5;
2560 
2561   PetscFunctionBegin;
2562   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2563   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2564   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2565   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2566   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2567   /* Check consistency */
2568   {
2569     PetscSection section;
2570     PetscBool    includesConstraints;
2571     PetscInt     m, m1;
2572 
2573     PetscCall(VecGetLocalSize(vec, &m1));
2574     PetscCall(DMGetGlobalSection(sectiondm, &section));
2575     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2576     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2577     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2578     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2579   }
2580   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2581   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2582   if (ishdf5) {
2583 #if defined(PETSC_HAVE_HDF5)
2584     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2585 #else
2586     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2587 #endif
2588   }
2589   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2590   PetscFunctionReturn(PETSC_SUCCESS);
2591 }
2592 
2593 /*@
2594   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2595 
2596   Collective
2597 
2598   Input Parameters:
2599 + dm        - The `DM` that represents the topology
2600 . viewer    - The `PetscViewer` that represents the on-disk vector data
2601 . sectiondm - The `DM` that contains the local section on which vec is defined
2602 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2603 - vec       - The local vector to set values of
2604 
2605   Level: advanced
2606 
2607   Notes:
2608   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2609 
2610   Calling sequence:
2611 .vb
2612        DMCreate(PETSC_COMM_WORLD, &dm);
2613        DMSetType(dm, DMPLEX);
2614        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2615        DMPlexTopologyLoad(dm, viewer, &sfX);
2616        DMClone(dm, &sectiondm);
2617        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2618        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2619        DMGetLocalVector(sectiondm, &vec);
2620        PetscObjectSetName((PetscObject)vec, "vec_name");
2621        DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2622        DMRestoreLocalVector(sectiondm, &vec);
2623        PetscSFDestroy(&lsf);
2624        PetscSFDestroy(&sfX);
2625        DMDestroy(&sectiondm);
2626        DMDestroy(&dm);
2627 .ve
2628 
2629 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2630           `PetscSF`, `PetscViewer`
2631 @*/
2632 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2633 {
2634   PetscBool ishdf5;
2635 
2636   PetscFunctionBegin;
2637   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2638   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2639   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2640   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2641   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2642   /* Check consistency */
2643   {
2644     PetscSection section;
2645     PetscBool    includesConstraints;
2646     PetscInt     m, m1;
2647 
2648     PetscCall(VecGetLocalSize(vec, &m1));
2649     PetscCall(DMGetLocalSection(sectiondm, &section));
2650     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2651     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2652     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2653     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2654   }
2655   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2656   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2657   if (ishdf5) {
2658 #if defined(PETSC_HAVE_HDF5)
2659     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2660 #else
2661     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2662 #endif
2663   }
2664   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2665   PetscFunctionReturn(PETSC_SUCCESS);
2666 }
2667 
2668 PetscErrorCode DMDestroy_Plex(DM dm)
2669 {
2670   DM_Plex *mesh = (DM_Plex *)dm->data;
2671 
2672   PetscFunctionBegin;
2673   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2674   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2675   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2676   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2677   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerivativeBoundaryValues_C", NULL));
2678   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2679   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2680   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2681   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2682   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2683   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2684   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2685   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2686   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetUseCeed_C", NULL));
2687   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetUseCeed_C", NULL));
2688   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", NULL));
2689   if (--mesh->refct > 0) PetscFunctionReturn(PETSC_SUCCESS);
2690   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2691   PetscCall(PetscFree(mesh->cones));
2692   PetscCall(PetscFree(mesh->coneOrientations));
2693   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2694   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2695   PetscCall(PetscFree(mesh->supports));
2696   PetscCall(PetscFree(mesh->cellTypes));
2697   PetscCall(DMPlexTransformDestroy(&mesh->tr));
2698   PetscCall(PetscFree(mesh->tetgenOpts));
2699   PetscCall(PetscFree(mesh->triangleOpts));
2700   PetscCall(PetscFree(mesh->transformType));
2701   PetscCall(PetscFree(mesh->distributionName));
2702   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2703   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2704   PetscCall(ISDestroy(&mesh->subpointIS));
2705   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2706   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2707   PetscCall(PetscSFDestroy(&mesh->periodic.face_sf));
2708   PetscCall(PetscSFDestroy(&mesh->periodic.composed_sf));
2709   PetscCall(ISDestroy(&mesh->periodic.periodic_points));
2710   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2711   PetscCall(ISDestroy(&mesh->anchorIS));
2712   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2713   PetscCall(PetscFree(mesh->parents));
2714   PetscCall(PetscFree(mesh->childIDs));
2715   PetscCall(PetscSectionDestroy(&mesh->childSection));
2716   PetscCall(PetscFree(mesh->children));
2717   PetscCall(DMDestroy(&mesh->referenceTree));
2718   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2719   PetscCall(PetscFree(mesh->neighbors));
2720   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2721   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2722   PetscCall(PetscFree(mesh));
2723   PetscFunctionReturn(PETSC_SUCCESS);
2724 }
2725 
2726 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2727 {
2728   PetscSection           sectionGlobal;
2729   PetscInt               bs = -1, mbs;
2730   PetscInt               localSize, localStart = 0;
2731   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2732   MatType                mtype;
2733   ISLocalToGlobalMapping ltog;
2734 
2735   PetscFunctionBegin;
2736   PetscCall(MatInitializePackage());
2737   mtype = dm->mattype;
2738   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2739   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2740   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2741   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2742   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2743   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2744   PetscCall(MatSetType(*J, mtype));
2745   PetscCall(MatSetFromOptions(*J));
2746   PetscCall(MatGetBlockSize(*J, &mbs));
2747   if (mbs > 1) bs = mbs;
2748   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2749   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2750   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2751   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2752   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2753   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2754   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2755   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2756   if (!isShell) {
2757     PetscBool fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2758     PetscInt *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2759     PetscInt  pStart, pEnd, p, dof, cdof, num_fields;
2760 
2761     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2762 
2763     PetscCall(PetscCalloc1(localSize, &pblocks));
2764     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2765     PetscCall(PetscSectionGetNumFields(sectionGlobal, &num_fields));
2766     for (p = pStart; p < pEnd; ++p) {
2767       switch (dm->blocking_type) {
2768       case DM_BLOCKING_TOPOLOGICAL_POINT: { // One block per topological point
2769         PetscInt bdof, offset;
2770 
2771         PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2772         PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2773         PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2774         for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = dof - cdof;
2775         dof  = dof < 0 ? -(dof + 1) : dof;
2776         bdof = cdof && (dof - cdof) ? 1 : dof;
2777         if (dof) {
2778           if (bs < 0) {
2779             bs = bdof;
2780           } else if (bs != bdof) {
2781             bs = 1;
2782           }
2783         }
2784       } break;
2785       case DM_BLOCKING_FIELD_NODE: {
2786         for (PetscInt field = 0; field < num_fields; field++) {
2787           PetscInt num_comp, bdof, offset;
2788           PetscCall(PetscSectionGetFieldComponents(sectionGlobal, field, &num_comp));
2789           PetscCall(PetscSectionGetFieldDof(sectionGlobal, p, field, &dof));
2790           if (dof < 0) continue;
2791           PetscCall(PetscSectionGetFieldOffset(sectionGlobal, p, field, &offset));
2792           PetscCall(PetscSectionGetFieldConstraintDof(sectionGlobal, p, field, &cdof));
2793           PetscAssert(dof % num_comp == 0, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " field %" PetscInt_FMT " has %" PetscInt_FMT " dof, not divisible by %" PetscInt_FMT " component ", p, field, dof, num_comp);
2794           PetscInt num_nodes = dof / num_comp;
2795           for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = (dof - cdof) / num_nodes;
2796           // Handle possibly constant block size (unlikely)
2797           bdof = cdof && (dof - cdof) ? 1 : dof;
2798           if (dof) {
2799             if (bs < 0) {
2800               bs = bdof;
2801             } else if (bs != bdof) {
2802               bs = 1;
2803             }
2804           }
2805         }
2806       } break;
2807       }
2808     }
2809     /* Must have same blocksize on all procs (some might have no points) */
2810     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2811     bsLocal[1] = bs;
2812     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
2813     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2814     else bs = bsMinMax[0];
2815     bs = PetscMax(1, bs);
2816     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
2817     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2818       PetscCall(MatSetBlockSize(*J, bs));
2819       PetscCall(MatSetUp(*J));
2820     } else {
2821       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
2822       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2823       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2824     }
2825     { // Consolidate blocks
2826       PetscInt nblocks = 0;
2827       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
2828         if (pblocks[i] == 0) continue;
2829         pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2830         for (PetscInt j = 1; j < pblocks[i]; j++) PetscCheck(pblocks[i + j] == pblocks[i], PETSC_COMM_SELF, PETSC_ERR_PLIB, "Block of size %" PetscInt_FMT " mismatches entry %" PetscInt_FMT, pblocks[i], pblocks[i + j]);
2831       }
2832       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2833     }
2834     PetscCall(PetscFree(pblocks));
2835   }
2836   PetscCall(MatSetDM(*J, dm));
2837   PetscFunctionReturn(PETSC_SUCCESS);
2838 }
2839 
2840 /*@
2841   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2842 
2843   Not Collective
2844 
2845   Input Parameter:
2846 . dm - The `DMPLEX`
2847 
2848   Output Parameter:
2849 . subsection - The subdomain section
2850 
2851   Level: developer
2852 
2853 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscSection`
2854 @*/
2855 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2856 {
2857   DM_Plex *mesh = (DM_Plex *)dm->data;
2858 
2859   PetscFunctionBegin;
2860   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2861   if (!mesh->subdomainSection) {
2862     PetscSection section;
2863     PetscSF      sf;
2864 
2865     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
2866     PetscCall(DMGetLocalSection(dm, &section));
2867     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
2868     PetscCall(PetscSFDestroy(&sf));
2869   }
2870   *subsection = mesh->subdomainSection;
2871   PetscFunctionReturn(PETSC_SUCCESS);
2872 }
2873 
2874 /*@
2875   DMPlexGetChart - Return the interval for all mesh points [`pStart`, `pEnd`)
2876 
2877   Not Collective
2878 
2879   Input Parameter:
2880 . dm - The `DMPLEX`
2881 
2882   Output Parameters:
2883 + pStart - The first mesh point
2884 - pEnd   - The upper bound for mesh points
2885 
2886   Level: beginner
2887 
2888 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`
2889 @*/
2890 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
2891 {
2892   DM_Plex *mesh = (DM_Plex *)dm->data;
2893 
2894   PetscFunctionBegin;
2895   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2896   if (mesh->tr) PetscCall(DMPlexTransformGetChart(mesh->tr, pStart, pEnd));
2897   else PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
2898   PetscFunctionReturn(PETSC_SUCCESS);
2899 }
2900 
2901 /*@
2902   DMPlexSetChart - Set the interval for all mesh points [`pStart`, `pEnd`)
2903 
2904   Not Collective
2905 
2906   Input Parameters:
2907 + dm     - The `DMPLEX`
2908 . pStart - The first mesh point
2909 - pEnd   - The upper bound for mesh points
2910 
2911   Level: beginner
2912 
2913 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetChart()`
2914 @*/
2915 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
2916 {
2917   DM_Plex *mesh = (DM_Plex *)dm->data;
2918 
2919   PetscFunctionBegin;
2920   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2921   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
2922   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
2923   PetscCall(PetscFree(mesh->cellTypes));
2924   PetscFunctionReturn(PETSC_SUCCESS);
2925 }
2926 
2927 /*@
2928   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
2929 
2930   Not Collective
2931 
2932   Input Parameters:
2933 + dm - The `DMPLEX`
2934 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
2935 
2936   Output Parameter:
2937 . size - The cone size for point `p`
2938 
2939   Level: beginner
2940 
2941 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
2942 @*/
2943 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
2944 {
2945   DM_Plex *mesh = (DM_Plex *)dm->data;
2946 
2947   PetscFunctionBegin;
2948   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2949   PetscAssertPointer(size, 3);
2950   if (mesh->tr) PetscCall(DMPlexTransformGetConeSize(mesh->tr, p, size));
2951   else PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
2952   PetscFunctionReturn(PETSC_SUCCESS);
2953 }
2954 
2955 /*@
2956   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
2957 
2958   Not Collective
2959 
2960   Input Parameters:
2961 + dm   - The `DMPLEX`
2962 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
2963 - size - The cone size for point `p`
2964 
2965   Level: beginner
2966 
2967   Note:
2968   This should be called after `DMPlexSetChart()`.
2969 
2970 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2971 @*/
2972 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
2973 {
2974   DM_Plex *mesh = (DM_Plex *)dm->data;
2975 
2976   PetscFunctionBegin;
2977   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2978   PetscCheck(!mesh->tr, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Cannot call DMPlexSetConeSize() on a mesh with a transform defined.");
2979   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
2980   PetscFunctionReturn(PETSC_SUCCESS);
2981 }
2982 
2983 /*@C
2984   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
2985 
2986   Not Collective
2987 
2988   Input Parameters:
2989 + dm - The `DMPLEX`
2990 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
2991 
2992   Output Parameter:
2993 . cone - An array of points which are on the in-edges for point `p`
2994 
2995   Level: beginner
2996 
2997   Fortran Notes:
2998   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
2999   `DMPlexRestoreCone()` is not needed/available in C.
3000 
3001 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`, `DMPlexRestoreCone()`
3002 @*/
3003 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
3004 {
3005   DM_Plex *mesh = (DM_Plex *)dm->data;
3006   PetscInt off;
3007 
3008   PetscFunctionBegin;
3009   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3010   PetscAssertPointer(cone, 3);
3011   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3012   *cone = &mesh->cones[off];
3013   PetscFunctionReturn(PETSC_SUCCESS);
3014 }
3015 
3016 /*@C
3017   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
3018 
3019   Not Collective
3020 
3021   Input Parameters:
3022 + dm - The `DMPLEX`
3023 - p  - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3024 
3025   Output Parameters:
3026 + pConesSection - `PetscSection` describing the layout of `pCones`
3027 - pCones        - An array of points which are on the in-edges for the point set `p`
3028 
3029   Level: intermediate
3030 
3031 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`, `PetscSection`, `IS`
3032 @*/
3033 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
3034 {
3035   PetscSection cs, newcs;
3036   PetscInt    *cones;
3037   PetscInt    *newarr = NULL;
3038   PetscInt     n;
3039 
3040   PetscFunctionBegin;
3041   PetscCall(DMPlexGetCones(dm, &cones));
3042   PetscCall(DMPlexGetConeSection(dm, &cs));
3043   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
3044   if (pConesSection) *pConesSection = newcs;
3045   if (pCones) {
3046     PetscCall(PetscSectionGetStorageSize(newcs, &n));
3047     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
3048   }
3049   PetscFunctionReturn(PETSC_SUCCESS);
3050 }
3051 
3052 /*@
3053   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
3054 
3055   Not Collective
3056 
3057   Input Parameters:
3058 + dm     - The `DMPLEX`
3059 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3060 
3061   Output Parameter:
3062 . expandedPoints - An array of vertices recursively expanded from input points
3063 
3064   Level: advanced
3065 
3066   Notes:
3067   Like `DMPlexGetConeRecursive()` but returns only the 0-depth `IS` (i.e. vertices only) and no sections.
3068 
3069   There is no corresponding Restore function, just call `ISDestroy()` on the returned `IS` to deallocate.
3070 
3071 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`,
3072           `DMPlexGetDepth()`, `IS`
3073 @*/
3074 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
3075 {
3076   IS      *expandedPointsAll;
3077   PetscInt depth;
3078 
3079   PetscFunctionBegin;
3080   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3081   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3082   PetscAssertPointer(expandedPoints, 3);
3083   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3084   *expandedPoints = expandedPointsAll[0];
3085   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
3086   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3087   PetscFunctionReturn(PETSC_SUCCESS);
3088 }
3089 
3090 /*@
3091   DMPlexGetConeRecursive - Expand each given point into its cone points and do that recursively until we end up just with vertices (DAG points of depth 0, i.e. without cones).
3092 
3093   Not Collective
3094 
3095   Input Parameters:
3096 + dm     - The `DMPLEX`
3097 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3098 
3099   Output Parameters:
3100 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3101 . expandedPoints - (optional) An array of index sets with recursively expanded cones
3102 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3103 
3104   Level: advanced
3105 
3106   Notes:
3107   Like `DMPlexGetConeTuple()` but recursive.
3108 
3109   Array `expandedPoints` has size equal to `depth`. Each `expandedPoints`[d] contains DAG points with maximum depth d, recursively cone-wise expanded from the input points.
3110   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
3111 
3112   Array section has size equal to `depth`.  Each `PetscSection` `sections`[d] realizes mapping from `expandedPoints`[d+1] (section points) to `expandedPoints`[d] (section dofs) as follows\:
3113   (1) DAG points in `expandedPoints`[d+1] with `depth` d+1 to their cone points in `expandedPoints`[d];
3114   (2) DAG points in `expandedPoints`[d+1] with `depth` in [0,d] to the same points in `expandedPoints`[d].
3115 
3116 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3117           `DMPlexGetDepth()`, `PetscSection`, `IS`
3118 @*/
3119 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3120 {
3121   const PetscInt *arr0 = NULL, *cone = NULL;
3122   PetscInt       *arr = NULL, *newarr = NULL;
3123   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
3124   IS             *expandedPoints_;
3125   PetscSection   *sections_;
3126 
3127   PetscFunctionBegin;
3128   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3129   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3130   if (depth) PetscAssertPointer(depth, 3);
3131   if (expandedPoints) PetscAssertPointer(expandedPoints, 4);
3132   if (sections) PetscAssertPointer(sections, 5);
3133   PetscCall(ISGetLocalSize(points, &n));
3134   PetscCall(ISGetIndices(points, &arr0));
3135   PetscCall(DMPlexGetDepth(dm, &depth_));
3136   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
3137   PetscCall(PetscCalloc1(depth_, &sections_));
3138   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
3139   for (d = depth_ - 1; d >= 0; d--) {
3140     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
3141     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
3142     for (i = 0; i < n; i++) {
3143       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
3144       if (arr[i] >= start && arr[i] < end) {
3145         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
3146         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
3147       } else {
3148         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
3149       }
3150     }
3151     PetscCall(PetscSectionSetUp(sections_[d]));
3152     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
3153     PetscCall(PetscMalloc1(newn, &newarr));
3154     for (i = 0; i < n; i++) {
3155       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
3156       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
3157       if (cn > 1) {
3158         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
3159         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
3160       } else {
3161         newarr[co] = arr[i];
3162       }
3163     }
3164     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
3165     arr = newarr;
3166     n   = newn;
3167   }
3168   PetscCall(ISRestoreIndices(points, &arr0));
3169   *depth = depth_;
3170   if (expandedPoints) *expandedPoints = expandedPoints_;
3171   else {
3172     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
3173     PetscCall(PetscFree(expandedPoints_));
3174   }
3175   if (sections) *sections = sections_;
3176   else {
3177     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
3178     PetscCall(PetscFree(sections_));
3179   }
3180   PetscFunctionReturn(PETSC_SUCCESS);
3181 }
3182 
3183 /*@
3184   DMPlexRestoreConeRecursive - Deallocates arrays created by `DMPlexGetConeRecursive()`
3185 
3186   Not Collective
3187 
3188   Input Parameters:
3189 + dm     - The `DMPLEX`
3190 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3191 
3192   Output Parameters:
3193 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3194 . expandedPoints - (optional) An array of recursively expanded cones
3195 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3196 
3197   Level: advanced
3198 
3199   Note:
3200   See `DMPlexGetConeRecursive()`
3201 
3202 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3203           `DMPlexGetDepth()`, `IS`, `PetscSection`
3204 @*/
3205 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3206 {
3207   PetscInt d, depth_;
3208 
3209   PetscFunctionBegin;
3210   PetscCall(DMPlexGetDepth(dm, &depth_));
3211   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
3212   if (depth) *depth = 0;
3213   if (expandedPoints) {
3214     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
3215     PetscCall(PetscFree(*expandedPoints));
3216   }
3217   if (sections) {
3218     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
3219     PetscCall(PetscFree(*sections));
3220   }
3221   PetscFunctionReturn(PETSC_SUCCESS);
3222 }
3223 
3224 /*@
3225   DMPlexSetCone - Set the points on the in-edges for this point in the DAG; that is these are the points that cover the specific point
3226 
3227   Not Collective
3228 
3229   Input Parameters:
3230 + dm   - The `DMPLEX`
3231 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3232 - cone - An array of points which are on the in-edges for point `p`
3233 
3234   Level: beginner
3235 
3236   Note:
3237   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3238 
3239 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3240 @*/
3241 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3242 {
3243   DM_Plex *mesh = (DM_Plex *)dm->data;
3244   PetscInt dof, off, c;
3245 
3246   PetscFunctionBegin;
3247   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3248   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3249   if (dof) PetscAssertPointer(cone, 3);
3250   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3251   if (PetscDefined(USE_DEBUG)) {
3252     PetscInt pStart, pEnd;
3253     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3254     PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3255     for (c = 0; c < dof; ++c) {
3256       PetscCheck(!(cone[c] < pStart) && !(cone[c] >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", cone[c], pStart, pEnd);
3257       mesh->cones[off + c] = cone[c];
3258     }
3259   } else {
3260     for (c = 0; c < dof; ++c) mesh->cones[off + c] = cone[c];
3261   }
3262   PetscFunctionReturn(PETSC_SUCCESS);
3263 }
3264 
3265 /*@C
3266   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3267 
3268   Not Collective
3269 
3270   Input Parameters:
3271 + dm - The `DMPLEX`
3272 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3273 
3274   Output Parameter:
3275 . coneOrientation - An array of orientations which are on the in-edges for point `p`. An orientation is an
3276                     integer giving the prescription for cone traversal.
3277 
3278   Level: beginner
3279 
3280   Note:
3281   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3282   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3283   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3284   with the identity.
3285 
3286   Fortran Notes:
3287   You must also call `DMPlexRestoreConeOrientation()` after you finish using the returned array.
3288   `DMPlexRestoreConeOrientation()` is not needed/available in C.
3289 
3290 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3291 @*/
3292 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3293 {
3294   DM_Plex *mesh = (DM_Plex *)dm->data;
3295   PetscInt off;
3296 
3297   PetscFunctionBegin;
3298   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3299   if (PetscDefined(USE_DEBUG)) {
3300     PetscInt dof;
3301     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3302     if (dof) PetscAssertPointer(coneOrientation, 3);
3303   }
3304   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3305 
3306   *coneOrientation = &mesh->coneOrientations[off];
3307   PetscFunctionReturn(PETSC_SUCCESS);
3308 }
3309 
3310 /*@
3311   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3312 
3313   Not Collective
3314 
3315   Input Parameters:
3316 + dm              - The `DMPLEX`
3317 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3318 - coneOrientation - An array of orientations
3319 
3320   Level: beginner
3321 
3322   Notes:
3323   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3324 
3325   The meaning of coneOrientation is detailed in `DMPlexGetConeOrientation()`.
3326 
3327 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3328 @*/
3329 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3330 {
3331   DM_Plex *mesh = (DM_Plex *)dm->data;
3332   PetscInt pStart, pEnd;
3333   PetscInt dof, off, c;
3334 
3335   PetscFunctionBegin;
3336   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3337   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3338   if (dof) PetscAssertPointer(coneOrientation, 3);
3339   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3340   if (PetscDefined(USE_DEBUG)) {
3341     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3342     PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3343     for (c = 0; c < dof; ++c) {
3344       PetscInt cdof, o = coneOrientation[c];
3345 
3346       PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3347       PetscCheck(!o || (o >= -(cdof + 1) && o < cdof), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone orientation %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ". %" PetscInt_FMT ")", o, -(cdof + 1), cdof);
3348       mesh->coneOrientations[off + c] = o;
3349     }
3350   } else {
3351     for (c = 0; c < dof; ++c) mesh->coneOrientations[off + c] = coneOrientation[c];
3352   }
3353   PetscFunctionReturn(PETSC_SUCCESS);
3354 }
3355 
3356 /*@
3357   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3358 
3359   Not Collective
3360 
3361   Input Parameters:
3362 + dm        - The `DMPLEX`
3363 . p         - The point, which must lie in the chart set with `DMPlexSetChart()`
3364 . conePos   - The local index in the cone where the point should be put
3365 - conePoint - The mesh point to insert
3366 
3367   Level: beginner
3368 
3369 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3370 @*/
3371 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3372 {
3373   DM_Plex *mesh = (DM_Plex *)dm->data;
3374   PetscInt pStart, pEnd;
3375   PetscInt dof, off;
3376 
3377   PetscFunctionBegin;
3378   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3379   if (PetscDefined(USE_DEBUG)) {
3380     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3381     PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3382     PetscCheck(!(conePoint < pStart) && !(conePoint >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", conePoint, pStart, pEnd);
3383     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3384     PetscCheck(!(conePos < 0) && !(conePos >= dof), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone position %" PetscInt_FMT " of point %" PetscInt_FMT " is not in the valid range [0, %" PetscInt_FMT ")", conePos, p, dof);
3385   }
3386   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3387   mesh->cones[off + conePos] = conePoint;
3388   PetscFunctionReturn(PETSC_SUCCESS);
3389 }
3390 
3391 /*@
3392   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3393 
3394   Not Collective
3395 
3396   Input Parameters:
3397 + dm              - The `DMPLEX`
3398 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3399 . conePos         - The local index in the cone where the point should be put
3400 - coneOrientation - The point orientation to insert
3401 
3402   Level: beginner
3403 
3404   Note:
3405   The meaning of coneOrientation values is detailed in `DMPlexGetConeOrientation()`.
3406 
3407 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3408 @*/
3409 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3410 {
3411   DM_Plex *mesh = (DM_Plex *)dm->data;
3412   PetscInt pStart, pEnd;
3413   PetscInt dof, off;
3414 
3415   PetscFunctionBegin;
3416   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3417   if (PetscDefined(USE_DEBUG)) {
3418     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3419     PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3420     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3421     PetscCheck(!(conePos < 0) && !(conePos >= dof), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Cone position %" PetscInt_FMT " of point %" PetscInt_FMT " is not in the valid range [0, %" PetscInt_FMT ")", conePos, p, dof);
3422   }
3423   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3424   mesh->coneOrientations[off + conePos] = coneOrientation;
3425   PetscFunctionReturn(PETSC_SUCCESS);
3426 }
3427 
3428 /*@C
3429   DMPlexGetOrientedCone - Return the points and orientations on the in-edges for this point in the DAG
3430 
3431   Not collective
3432 
3433   Input Parameters:
3434 + dm - The DMPlex
3435 - p  - The point, which must lie in the chart set with DMPlexSetChart()
3436 
3437   Output Parameters:
3438 + cone - An array of points which are on the in-edges for point `p`
3439 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3440         integer giving the prescription for cone traversal.
3441 
3442   Level: beginner
3443 
3444   Notes:
3445   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3446   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3447   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3448   with the identity.
3449 
3450   Fortran Notes:
3451   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3452   `DMPlexRestoreCone()` is not needed/available in C.
3453 
3454 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3455 @*/
3456 PetscErrorCode DMPlexGetOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3457 {
3458   DM_Plex *mesh = (DM_Plex *)dm->data;
3459 
3460   PetscFunctionBegin;
3461   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3462   if (mesh->tr) {
3463     PetscCall(DMPlexTransformGetCone(mesh->tr, p, cone, ornt));
3464   } else {
3465     PetscInt off;
3466     if (PetscDefined(USE_DEBUG)) {
3467       PetscInt dof;
3468       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3469       if (dof) {
3470         if (cone) PetscAssertPointer(cone, 3);
3471         if (ornt) PetscAssertPointer(ornt, 4);
3472       }
3473     }
3474     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3475     if (cone) *cone = mesh->cones ? mesh->cones + off : NULL; // NULL + 0 is UB
3476     if (ornt) *ornt = mesh->coneOrientations ? mesh->coneOrientations + off : NULL;
3477   }
3478   PetscFunctionReturn(PETSC_SUCCESS);
3479 }
3480 
3481 /*@C
3482   DMPlexRestoreOrientedCone - Restore the points and orientations on the in-edges for this point in the DAG
3483 
3484   Not Collective
3485 
3486   Input Parameters:
3487 + dm   - The DMPlex
3488 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3489 . cone - An array of points which are on the in-edges for point p
3490 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3491         integer giving the prescription for cone traversal.
3492 
3493   Level: beginner
3494 
3495   Notes:
3496   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3497   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3498   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3499   with the identity.
3500 
3501   Fortran Notes:
3502   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3503   `DMPlexRestoreCone()` is not needed/available in C.
3504 
3505 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3506 @*/
3507 PetscErrorCode DMPlexRestoreOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3508 {
3509   DM_Plex *mesh = (DM_Plex *)dm->data;
3510 
3511   PetscFunctionBegin;
3512   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3513   if (mesh->tr) PetscCall(DMPlexTransformRestoreCone(mesh->tr, p, cone, ornt));
3514   PetscFunctionReturn(PETSC_SUCCESS);
3515 }
3516 
3517 /*@
3518   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3519 
3520   Not Collective
3521 
3522   Input Parameters:
3523 + dm - The `DMPLEX`
3524 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3525 
3526   Output Parameter:
3527 . size - The support size for point `p`
3528 
3529   Level: beginner
3530 
3531 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3532 @*/
3533 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3534 {
3535   DM_Plex *mesh = (DM_Plex *)dm->data;
3536 
3537   PetscFunctionBegin;
3538   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3539   PetscAssertPointer(size, 3);
3540   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3541   PetscFunctionReturn(PETSC_SUCCESS);
3542 }
3543 
3544 /*@
3545   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3546 
3547   Not Collective
3548 
3549   Input Parameters:
3550 + dm   - The `DMPLEX`
3551 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3552 - size - The support size for point `p`
3553 
3554   Level: beginner
3555 
3556   Note:
3557   This should be called after `DMPlexSetChart()`.
3558 
3559 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3560 @*/
3561 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3562 {
3563   DM_Plex *mesh = (DM_Plex *)dm->data;
3564 
3565   PetscFunctionBegin;
3566   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3567   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3568   PetscFunctionReturn(PETSC_SUCCESS);
3569 }
3570 
3571 /*@C
3572   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3573 
3574   Not Collective
3575 
3576   Input Parameters:
3577 + dm - The `DMPLEX`
3578 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3579 
3580   Output Parameter:
3581 . support - An array of points which are on the out-edges for point `p`
3582 
3583   Level: beginner
3584 
3585   Fortran Notes:
3586   You must also call `DMPlexRestoreSupport()` after you finish using the returned array.
3587   `DMPlexRestoreSupport()` is not needed/available in C.
3588 
3589 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3590 @*/
3591 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3592 {
3593   DM_Plex *mesh = (DM_Plex *)dm->data;
3594   PetscInt off;
3595 
3596   PetscFunctionBegin;
3597   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3598   PetscAssertPointer(support, 3);
3599   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3600   *support = mesh->supports ? mesh->supports + off : NULL; //NULL + 0 is UB
3601   PetscFunctionReturn(PETSC_SUCCESS);
3602 }
3603 
3604 /*@
3605   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3606 
3607   Not Collective
3608 
3609   Input Parameters:
3610 + dm      - The `DMPLEX`
3611 . p       - The point, which must lie in the chart set with `DMPlexSetChart()`
3612 - support - An array of points which are on the out-edges for point `p`
3613 
3614   Level: beginner
3615 
3616   Note:
3617   This should be called after all calls to `DMPlexSetSupportSize()` and `DMSetUp()`.
3618 
3619 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3620 @*/
3621 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3622 {
3623   DM_Plex *mesh = (DM_Plex *)dm->data;
3624   PetscInt pStart, pEnd;
3625   PetscInt dof, off, c;
3626 
3627   PetscFunctionBegin;
3628   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3629   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3630   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3631   if (dof) PetscAssertPointer(support, 3);
3632   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3633   PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3634   for (c = 0; c < dof; ++c) {
3635     PetscCheck(!(support[c] < pStart) && !(support[c] >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Support point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", support[c], pStart, pEnd);
3636     mesh->supports[off + c] = support[c];
3637   }
3638   PetscFunctionReturn(PETSC_SUCCESS);
3639 }
3640 
3641 /*@
3642   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3643 
3644   Not Collective
3645 
3646   Input Parameters:
3647 + dm           - The `DMPLEX`
3648 . p            - The point, which must lie in the chart set with `DMPlexSetChart()`
3649 . supportPos   - The local index in the cone where the point should be put
3650 - supportPoint - The mesh point to insert
3651 
3652   Level: beginner
3653 
3654 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3655 @*/
3656 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3657 {
3658   DM_Plex *mesh = (DM_Plex *)dm->data;
3659   PetscInt pStart, pEnd;
3660   PetscInt dof, off;
3661 
3662   PetscFunctionBegin;
3663   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3664   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3665   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3666   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3667   PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3668   PetscCheck(!(supportPoint < pStart) && !(supportPoint >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Support point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", supportPoint, pStart, pEnd);
3669   PetscCheck(supportPos < dof, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Support position %" PetscInt_FMT " of point %" PetscInt_FMT " is not in the valid range [0, %" PetscInt_FMT ")", supportPos, p, dof);
3670   mesh->supports[off + supportPos] = supportPoint;
3671   PetscFunctionReturn(PETSC_SUCCESS);
3672 }
3673 
3674 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3675 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3676 {
3677   switch (ct) {
3678   case DM_POLYTOPE_SEGMENT:
3679     if (o == -1) return -2;
3680     break;
3681   case DM_POLYTOPE_TRIANGLE:
3682     if (o == -3) return -1;
3683     if (o == -2) return -3;
3684     if (o == -1) return -2;
3685     break;
3686   case DM_POLYTOPE_QUADRILATERAL:
3687     if (o == -4) return -2;
3688     if (o == -3) return -1;
3689     if (o == -2) return -4;
3690     if (o == -1) return -3;
3691     break;
3692   default:
3693     return o;
3694   }
3695   return o;
3696 }
3697 
3698 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3699 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3700 {
3701   switch (ct) {
3702   case DM_POLYTOPE_SEGMENT:
3703     if ((o == -2) || (o == 1)) return -1;
3704     if (o == -1) return 0;
3705     break;
3706   case DM_POLYTOPE_TRIANGLE:
3707     if (o == -3) return -2;
3708     if (o == -2) return -1;
3709     if (o == -1) return -3;
3710     break;
3711   case DM_POLYTOPE_QUADRILATERAL:
3712     if (o == -4) return -2;
3713     if (o == -3) return -1;
3714     if (o == -2) return -4;
3715     if (o == -1) return -3;
3716     break;
3717   default:
3718     return o;
3719   }
3720   return o;
3721 }
3722 
3723 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3724 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3725 {
3726   PetscInt pStart, pEnd, p;
3727 
3728   PetscFunctionBegin;
3729   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3730   for (p = pStart; p < pEnd; ++p) {
3731     const PetscInt *cone, *ornt;
3732     PetscInt        coneSize, c;
3733 
3734     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3735     PetscCall(DMPlexGetCone(dm, p, &cone));
3736     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3737     for (c = 0; c < coneSize; ++c) {
3738       DMPolytopeType ct;
3739       const PetscInt o = ornt[c];
3740 
3741       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3742       switch (ct) {
3743       case DM_POLYTOPE_SEGMENT:
3744         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3745         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3746         break;
3747       case DM_POLYTOPE_TRIANGLE:
3748         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3749         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3750         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3751         break;
3752       case DM_POLYTOPE_QUADRILATERAL:
3753         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3754         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3755         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3756         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3757         break;
3758       default:
3759         break;
3760       }
3761     }
3762   }
3763   PetscFunctionReturn(PETSC_SUCCESS);
3764 }
3765 
3766 static inline PetscErrorCode DMPlexGetTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3767 {
3768   DM_Plex *mesh = (DM_Plex *)dm->data;
3769 
3770   PetscFunctionBeginHot;
3771   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3772     if (useCone) {
3773       PetscCall(DMPlexGetConeSize(dm, p, size));
3774       PetscCall(DMPlexGetOrientedCone(dm, p, arr, ornt));
3775     } else {
3776       PetscCall(DMPlexGetSupportSize(dm, p, size));
3777       PetscCall(DMPlexGetSupport(dm, p, arr));
3778     }
3779   } else {
3780     if (useCone) {
3781       const PetscSection s   = mesh->coneSection;
3782       const PetscInt     ps  = p - s->pStart;
3783       const PetscInt     off = s->atlasOff[ps];
3784 
3785       *size = s->atlasDof[ps];
3786       *arr  = mesh->cones + off;
3787       *ornt = mesh->coneOrientations + off;
3788     } else {
3789       const PetscSection s   = mesh->supportSection;
3790       const PetscInt     ps  = p - s->pStart;
3791       const PetscInt     off = s->atlasOff[ps];
3792 
3793       *size = s->atlasDof[ps];
3794       *arr  = mesh->supports + off;
3795     }
3796   }
3797   PetscFunctionReturn(PETSC_SUCCESS);
3798 }
3799 
3800 static inline PetscErrorCode DMPlexRestoreTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3801 {
3802   DM_Plex *mesh = (DM_Plex *)dm->data;
3803 
3804   PetscFunctionBeginHot;
3805   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3806     if (useCone) PetscCall(DMPlexRestoreOrientedCone(dm, p, arr, ornt));
3807   }
3808   PetscFunctionReturn(PETSC_SUCCESS);
3809 }
3810 
3811 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3812 {
3813   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3814   PetscInt       *closure;
3815   const PetscInt *tmp = NULL, *tmpO = NULL;
3816   PetscInt        off = 0, tmpSize, t;
3817 
3818   PetscFunctionBeginHot;
3819   if (ornt) {
3820     PetscCall(DMPlexGetCellType(dm, p, &ct));
3821     if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN || ct == DM_POLYTOPE_UNKNOWN_CELL || ct == DM_POLYTOPE_UNKNOWN_FACE) ct = DM_POLYTOPE_UNKNOWN;
3822   }
3823   if (*points) {
3824     closure = *points;
3825   } else {
3826     PetscInt maxConeSize, maxSupportSize;
3827     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3828     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
3829   }
3830   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3831   if (ct == DM_POLYTOPE_UNKNOWN) {
3832     closure[off++] = p;
3833     closure[off++] = 0;
3834     for (t = 0; t < tmpSize; ++t) {
3835       closure[off++] = tmp[t];
3836       closure[off++] = tmpO ? tmpO[t] : 0;
3837     }
3838   } else {
3839     const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, ornt);
3840 
3841     /* We assume that cells with a valid type have faces with a valid type */
3842     closure[off++] = p;
3843     closure[off++] = ornt;
3844     for (t = 0; t < tmpSize; ++t) {
3845       DMPolytopeType ft;
3846 
3847       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3848       closure[off++] = tmp[arr[t]];
3849       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3850     }
3851   }
3852   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3853   if (numPoints) *numPoints = tmpSize + 1;
3854   if (points) *points = closure;
3855   PetscFunctionReturn(PETSC_SUCCESS);
3856 }
3857 
3858 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
3859 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3860 {
3861   const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, o);
3862   const PetscInt *cone, *ornt;
3863   PetscInt       *pts, *closure = NULL;
3864   DMPolytopeType  ft;
3865   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3866   PetscInt        dim, coneSize, c, d, clSize, cl;
3867 
3868   PetscFunctionBeginHot;
3869   PetscCall(DMGetDimension(dm, &dim));
3870   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3871   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3872   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
3873   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
3874   maxSize       = PetscMax(coneSeries, supportSeries);
3875   if (*points) {
3876     pts = *points;
3877   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
3878   c        = 0;
3879   pts[c++] = point;
3880   pts[c++] = o;
3881   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
3882   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
3883   for (cl = 0; cl < clSize * 2; cl += 2) {
3884     pts[c++] = closure[cl];
3885     pts[c++] = closure[cl + 1];
3886   }
3887   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
3888   for (cl = 0; cl < clSize * 2; cl += 2) {
3889     pts[c++] = closure[cl];
3890     pts[c++] = closure[cl + 1];
3891   }
3892   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3893   for (d = 2; d < coneSize; ++d) {
3894     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
3895     pts[c++] = cone[arr[d * 2 + 0]];
3896     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
3897   }
3898   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3899   if (dim >= 3) {
3900     for (d = 2; d < coneSize; ++d) {
3901       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
3902       const PetscInt *fcone, *fornt;
3903       PetscInt        fconeSize, fc, i;
3904 
3905       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
3906       const PetscInt *farr = DMPolytopeTypeGetArrangment(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
3907       PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
3908       for (fc = 0; fc < fconeSize; ++fc) {
3909         const PetscInt cp = fcone[farr[fc * 2 + 0]];
3910         const PetscInt co = farr[fc * 2 + 1];
3911 
3912         for (i = 0; i < c; i += 2)
3913           if (pts[i] == cp) break;
3914         if (i == c) {
3915           PetscCall(DMPlexGetCellType(dm, cp, &ft));
3916           pts[c++] = cp;
3917           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
3918         }
3919       }
3920       PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
3921     }
3922   }
3923   *numPoints = c / 2;
3924   *points    = pts;
3925   PetscFunctionReturn(PETSC_SUCCESS);
3926 }
3927 
3928 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3929 {
3930   DMPolytopeType ct;
3931   PetscInt      *closure, *fifo;
3932   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
3933   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
3934   PetscInt       depth, maxSize;
3935 
3936   PetscFunctionBeginHot;
3937   PetscCall(DMPlexGetDepth(dm, &depth));
3938   if (depth == 1) {
3939     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
3940     PetscFunctionReturn(PETSC_SUCCESS);
3941   }
3942   PetscCall(DMPlexGetCellType(dm, p, &ct));
3943   if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN || ct == DM_POLYTOPE_UNKNOWN_CELL || ct == DM_POLYTOPE_UNKNOWN_FACE) ct = DM_POLYTOPE_UNKNOWN;
3944   if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
3945     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
3946     PetscFunctionReturn(PETSC_SUCCESS);
3947   }
3948   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3949   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
3950   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
3951   maxSize       = PetscMax(coneSeries, supportSeries);
3952   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3953   if (*points) {
3954     closure = *points;
3955   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
3956   closure[closureSize++] = p;
3957   closure[closureSize++] = ornt;
3958   fifo[fifoSize++]       = p;
3959   fifo[fifoSize++]       = ornt;
3960   fifo[fifoSize++]       = ct;
3961   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
3962   while (fifoSize - fifoStart) {
3963     const PetscInt       q    = fifo[fifoStart++];
3964     const PetscInt       o    = fifo[fifoStart++];
3965     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
3966     const PetscInt      *qarr = DMPolytopeTypeGetArrangment(qt, o);
3967     const PetscInt      *tmp, *tmpO = NULL;
3968     PetscInt             tmpSize, t;
3969 
3970     if (PetscDefined(USE_DEBUG)) {
3971       PetscInt nO = DMPolytopeTypeGetNumArrangments(qt) / 2;
3972       PetscCheck(!o || !(o >= nO || o < -nO), PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid orientation %" PetscInt_FMT " not in [%" PetscInt_FMT ",%" PetscInt_FMT ") for %s %" PetscInt_FMT, o, -nO, nO, DMPolytopeTypes[qt], q);
3973     }
3974     PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
3975     for (t = 0; t < tmpSize; ++t) {
3976       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
3977       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
3978       const PetscInt cp = tmp[ip];
3979       PetscCall(DMPlexGetCellType(dm, cp, &ct));
3980       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
3981       PetscInt       c;
3982 
3983       /* Check for duplicate */
3984       for (c = 0; c < closureSize; c += 2) {
3985         if (closure[c] == cp) break;
3986       }
3987       if (c == closureSize) {
3988         closure[closureSize++] = cp;
3989         closure[closureSize++] = co;
3990         fifo[fifoSize++]       = cp;
3991         fifo[fifoSize++]       = co;
3992         fifo[fifoSize++]       = ct;
3993       }
3994     }
3995     PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
3996   }
3997   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3998   if (numPoints) *numPoints = closureSize / 2;
3999   if (points) *points = closure;
4000   PetscFunctionReturn(PETSC_SUCCESS);
4001 }
4002 
4003 /*@C
4004   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
4005 
4006   Not Collective
4007 
4008   Input Parameters:
4009 + dm      - The `DMPLEX`
4010 . p       - The mesh point
4011 - useCone - `PETSC_TRUE` for the closure, otherwise return the star
4012 
4013   Input/Output Parameter:
4014 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
4015            if `NULL` on input, internal storage will be returned, otherwise the provided array is used
4016 
4017   Output Parameter:
4018 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
4019 
4020   Level: beginner
4021 
4022   Note:
4023   If using internal storage (points is `NULL` on input), each call overwrites the last output.
4024 
4025   Fortran Notes:
4026   The `numPoints` argument is not present in the Fortran binding since it is internal to the array.
4027 
4028 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4029 @*/
4030 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4031 {
4032   PetscFunctionBeginHot;
4033   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4034   if (numPoints) PetscAssertPointer(numPoints, 4);
4035   if (points) PetscAssertPointer(points, 5);
4036   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
4037   PetscFunctionReturn(PETSC_SUCCESS);
4038 }
4039 
4040 /*@C
4041   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
4042 
4043   Not Collective
4044 
4045   Input Parameters:
4046 + dm        - The `DMPLEX`
4047 . p         - The mesh point
4048 . useCone   - `PETSC_TRUE` for the closure, otherwise return the star
4049 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
4050 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
4051 
4052   Level: beginner
4053 
4054   Note:
4055   If not using internal storage (points is not `NULL` on input), this call is unnecessary
4056 
4057 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4058 @*/
4059 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4060 {
4061   PetscFunctionBeginHot;
4062   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4063   if (numPoints) *numPoints = 0;
4064   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
4065   PetscFunctionReturn(PETSC_SUCCESS);
4066 }
4067 
4068 /*@
4069   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
4070 
4071   Not Collective
4072 
4073   Input Parameter:
4074 . dm - The `DMPLEX`
4075 
4076   Output Parameters:
4077 + maxConeSize    - The maximum number of in-edges
4078 - maxSupportSize - The maximum number of out-edges
4079 
4080   Level: beginner
4081 
4082 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
4083 @*/
4084 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
4085 {
4086   DM_Plex *mesh = (DM_Plex *)dm->data;
4087 
4088   PetscFunctionBegin;
4089   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4090   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
4091   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
4092   PetscFunctionReturn(PETSC_SUCCESS);
4093 }
4094 
4095 PetscErrorCode DMSetUp_Plex(DM dm)
4096 {
4097   DM_Plex *mesh = (DM_Plex *)dm->data;
4098   PetscInt size, maxSupportSize;
4099 
4100   PetscFunctionBegin;
4101   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4102   PetscCall(PetscSectionSetUp(mesh->coneSection));
4103   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
4104   PetscCall(PetscMalloc1(size, &mesh->cones));
4105   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
4106   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4107   if (maxSupportSize) {
4108     PetscCall(PetscSectionSetUp(mesh->supportSection));
4109     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
4110     PetscCall(PetscMalloc1(size, &mesh->supports));
4111   }
4112   PetscFunctionReturn(PETSC_SUCCESS);
4113 }
4114 
4115 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
4116 {
4117   PetscFunctionBegin;
4118   if (subdm) PetscCall(DMClone(dm, subdm));
4119   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, is, subdm));
4120   if (subdm) (*subdm)->useNatural = dm->useNatural;
4121   if (dm->useNatural && dm->sfMigration) {
4122     PetscSF sfNatural;
4123 
4124     (*subdm)->sfMigration = dm->sfMigration;
4125     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
4126     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
4127     (*subdm)->sfNatural = sfNatural;
4128   }
4129   PetscFunctionReturn(PETSC_SUCCESS);
4130 }
4131 
4132 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
4133 {
4134   PetscInt i = 0;
4135 
4136   PetscFunctionBegin;
4137   PetscCall(DMClone(dms[0], superdm));
4138   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
4139   (*superdm)->useNatural = PETSC_FALSE;
4140   for (i = 0; i < len; i++) {
4141     if (dms[i]->useNatural && dms[i]->sfMigration) {
4142       PetscSF sfNatural;
4143 
4144       (*superdm)->sfMigration = dms[i]->sfMigration;
4145       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
4146       (*superdm)->useNatural = PETSC_TRUE;
4147       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
4148       (*superdm)->sfNatural = sfNatural;
4149       break;
4150     }
4151   }
4152   PetscFunctionReturn(PETSC_SUCCESS);
4153 }
4154 
4155 /*@
4156   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
4157 
4158   Not Collective
4159 
4160   Input Parameter:
4161 . dm - The `DMPLEX`
4162 
4163   Level: beginner
4164 
4165   Note:
4166   This should be called after all calls to `DMPlexSetCone()`
4167 
4168 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
4169 @*/
4170 PetscErrorCode DMPlexSymmetrize(DM dm)
4171 {
4172   DM_Plex  *mesh = (DM_Plex *)dm->data;
4173   PetscInt *offsets;
4174   PetscInt  supportSize;
4175   PetscInt  pStart, pEnd, p;
4176 
4177   PetscFunctionBegin;
4178   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4179   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
4180   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
4181   /* Calculate support sizes */
4182   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4183   for (p = pStart; p < pEnd; ++p) {
4184     PetscInt dof, off, c;
4185 
4186     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4187     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4188     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
4189   }
4190   PetscCall(PetscSectionSetUp(mesh->supportSection));
4191   /* Calculate supports */
4192   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
4193   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
4194   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
4195   for (p = pStart; p < pEnd; ++p) {
4196     PetscInt dof, off, c;
4197 
4198     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4199     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4200     for (c = off; c < off + dof; ++c) {
4201       const PetscInt q = mesh->cones[c];
4202       PetscInt       offS;
4203 
4204       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
4205 
4206       mesh->supports[offS + offsets[q]] = p;
4207       ++offsets[q];
4208     }
4209   }
4210   PetscCall(PetscFree(offsets));
4211   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
4212   PetscFunctionReturn(PETSC_SUCCESS);
4213 }
4214 
4215 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
4216 {
4217   IS stratumIS;
4218 
4219   PetscFunctionBegin;
4220   if (pStart >= pEnd) PetscFunctionReturn(PETSC_SUCCESS);
4221   if (PetscDefined(USE_DEBUG)) {
4222     PetscInt  qStart, qEnd, numLevels, level;
4223     PetscBool overlap = PETSC_FALSE;
4224     PetscCall(DMLabelGetNumValues(label, &numLevels));
4225     for (level = 0; level < numLevels; level++) {
4226       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4227       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
4228         overlap = PETSC_TRUE;
4229         break;
4230       }
4231     }
4232     PetscCheck(!overlap, PETSC_COMM_SELF, PETSC_ERR_PLIB, "New depth %" PetscInt_FMT " range [%" PetscInt_FMT ",%" PetscInt_FMT ") overlaps with depth %" PetscInt_FMT " range [%" PetscInt_FMT ",%" PetscInt_FMT ")", depth, pStart, pEnd, level, qStart, qEnd);
4233   }
4234   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
4235   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
4236   PetscCall(ISDestroy(&stratumIS));
4237   PetscFunctionReturn(PETSC_SUCCESS);
4238 }
4239 
4240 /*@
4241   DMPlexStratify - Computes the strata for all points in the `DMPLEX`
4242 
4243   Collective
4244 
4245   Input Parameter:
4246 . dm - The `DMPLEX`
4247 
4248   Level: beginner
4249 
4250   Notes:
4251   The strata group all points of the same grade, and this function calculates the strata. This
4252   grade can be seen as the height (or depth) of the point in the DAG.
4253 
4254   The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
4255   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram).
4256   Concretely, `DMPlexStratify()` creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
4257   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
4258   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through `DMPlexGetDepthLabel()` or `DMPlexGetDepthStratum()`, or
4259   manually via `DMGetLabel()`.  The height is defined implicitly by height = maxDimension - depth, and can be accessed
4260   via `DMPlexGetHeightStratum()`.  For example, cells have height 0 and faces have height 1.
4261 
4262   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
4263   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
4264   we had a mesh consisting of one triangle (c0) and three vertices (v0, v1, v2), and only one edge is on the boundary so we choose
4265   to interpolate only that one (e0), so that
4266 .vb
4267   cone(c0) = {e0, v2}
4268   cone(e0) = {v0, v1}
4269 .ve
4270   If `DMPlexStratify()` is run on this mesh, it will give depths
4271 .vb
4272    depth 0 = {v0, v1, v2}
4273    depth 1 = {e0, c0}
4274 .ve
4275   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
4276 
4277   `DMPlexStratify()` should be called after all calls to `DMPlexSymmetrize()`
4278 
4279 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
4280 @*/
4281 PetscErrorCode DMPlexStratify(DM dm)
4282 {
4283   DM_Plex *mesh = (DM_Plex *)dm->data;
4284   DMLabel  label;
4285   PetscInt pStart, pEnd, p;
4286   PetscInt numRoots = 0, numLeaves = 0;
4287 
4288   PetscFunctionBegin;
4289   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4290   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
4291 
4292   /* Create depth label */
4293   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4294   PetscCall(DMCreateLabel(dm, "depth"));
4295   PetscCall(DMPlexGetDepthLabel(dm, &label));
4296 
4297   {
4298     /* Initialize roots and count leaves */
4299     PetscInt sMin = PETSC_MAX_INT;
4300     PetscInt sMax = PETSC_MIN_INT;
4301     PetscInt coneSize, supportSize;
4302 
4303     for (p = pStart; p < pEnd; ++p) {
4304       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4305       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4306       if (!coneSize && supportSize) {
4307         sMin = PetscMin(p, sMin);
4308         sMax = PetscMax(p, sMax);
4309         ++numRoots;
4310       } else if (!supportSize && coneSize) {
4311         ++numLeaves;
4312       } else if (!supportSize && !coneSize) {
4313         /* Isolated points */
4314         sMin = PetscMin(p, sMin);
4315         sMax = PetscMax(p, sMax);
4316       }
4317     }
4318     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4319   }
4320 
4321   if (numRoots + numLeaves == (pEnd - pStart)) {
4322     PetscInt sMin = PETSC_MAX_INT;
4323     PetscInt sMax = PETSC_MIN_INT;
4324     PetscInt coneSize, supportSize;
4325 
4326     for (p = pStart; p < pEnd; ++p) {
4327       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4328       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4329       if (!supportSize && coneSize) {
4330         sMin = PetscMin(p, sMin);
4331         sMax = PetscMax(p, sMax);
4332       }
4333     }
4334     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4335   } else {
4336     PetscInt level = 0;
4337     PetscInt qStart, qEnd, q;
4338 
4339     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4340     while (qEnd > qStart) {
4341       PetscInt sMin = PETSC_MAX_INT;
4342       PetscInt sMax = PETSC_MIN_INT;
4343 
4344       for (q = qStart; q < qEnd; ++q) {
4345         const PetscInt *support;
4346         PetscInt        supportSize, s;
4347 
4348         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4349         PetscCall(DMPlexGetSupport(dm, q, &support));
4350         for (s = 0; s < supportSize; ++s) {
4351           sMin = PetscMin(support[s], sMin);
4352           sMax = PetscMax(support[s], sMax);
4353         }
4354       }
4355       PetscCall(DMLabelGetNumValues(label, &level));
4356       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4357       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4358     }
4359   }
4360   { /* just in case there is an empty process */
4361     PetscInt numValues, maxValues = 0, v;
4362 
4363     PetscCall(DMLabelGetNumValues(label, &numValues));
4364     PetscCall(MPIU_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4365     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4366   }
4367   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4368   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4369   PetscFunctionReturn(PETSC_SUCCESS);
4370 }
4371 
4372 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4373 {
4374   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4375   PetscInt       dim, depth, pheight, coneSize;
4376 
4377   PetscFunctionBeginHot;
4378   PetscCall(DMGetDimension(dm, &dim));
4379   PetscCall(DMPlexGetDepth(dm, &depth));
4380   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4381   pheight = depth - pdepth;
4382   if (depth <= 1) {
4383     switch (pdepth) {
4384     case 0:
4385       ct = DM_POLYTOPE_POINT;
4386       break;
4387     case 1:
4388       switch (coneSize) {
4389       case 2:
4390         ct = DM_POLYTOPE_SEGMENT;
4391         break;
4392       case 3:
4393         ct = DM_POLYTOPE_TRIANGLE;
4394         break;
4395       case 4:
4396         switch (dim) {
4397         case 2:
4398           ct = DM_POLYTOPE_QUADRILATERAL;
4399           break;
4400         case 3:
4401           ct = DM_POLYTOPE_TETRAHEDRON;
4402           break;
4403         default:
4404           break;
4405         }
4406         break;
4407       case 5:
4408         ct = DM_POLYTOPE_PYRAMID;
4409         break;
4410       case 6:
4411         ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4412         break;
4413       case 8:
4414         ct = DM_POLYTOPE_HEXAHEDRON;
4415         break;
4416       default:
4417         break;
4418       }
4419     }
4420   } else {
4421     if (pdepth == 0) {
4422       ct = DM_POLYTOPE_POINT;
4423     } else if (pheight == 0) {
4424       switch (dim) {
4425       case 1:
4426         switch (coneSize) {
4427         case 2:
4428           ct = DM_POLYTOPE_SEGMENT;
4429           break;
4430         default:
4431           break;
4432         }
4433         break;
4434       case 2:
4435         switch (coneSize) {
4436         case 3:
4437           ct = DM_POLYTOPE_TRIANGLE;
4438           break;
4439         case 4:
4440           ct = DM_POLYTOPE_QUADRILATERAL;
4441           break;
4442         default:
4443           break;
4444         }
4445         break;
4446       case 3:
4447         switch (coneSize) {
4448         case 4:
4449           ct = DM_POLYTOPE_TETRAHEDRON;
4450           break;
4451         case 5: {
4452           const PetscInt *cone;
4453           PetscInt        faceConeSize;
4454 
4455           PetscCall(DMPlexGetCone(dm, p, &cone));
4456           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4457           switch (faceConeSize) {
4458           case 3:
4459             ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4460             break;
4461           case 4:
4462             ct = DM_POLYTOPE_PYRAMID;
4463             break;
4464           }
4465         } break;
4466         case 6:
4467           ct = DM_POLYTOPE_HEXAHEDRON;
4468           break;
4469         default:
4470           break;
4471         }
4472         break;
4473       default:
4474         break;
4475       }
4476     } else if (pheight > 0) {
4477       switch (coneSize) {
4478       case 2:
4479         ct = DM_POLYTOPE_SEGMENT;
4480         break;
4481       case 3:
4482         ct = DM_POLYTOPE_TRIANGLE;
4483         break;
4484       case 4:
4485         ct = DM_POLYTOPE_QUADRILATERAL;
4486         break;
4487       default:
4488         break;
4489       }
4490     }
4491   }
4492   *pt = ct;
4493   PetscFunctionReturn(PETSC_SUCCESS);
4494 }
4495 
4496 /*@
4497   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4498 
4499   Collective
4500 
4501   Input Parameter:
4502 . dm - The `DMPLEX`
4503 
4504   Level: developer
4505 
4506   Note:
4507   This function is normally called automatically when a cell type is requested. It creates an
4508   internal `DMLabel` named "celltype" which can be directly accessed using `DMGetLabel()`. A user may disable
4509   automatic creation by creating the label manually, using `DMCreateLabel`(dm, "celltype").
4510 
4511   `DMPlexComputeCellTypes()` should be called after all calls to `DMPlexSymmetrize()` and `DMPlexStratify()`
4512 
4513 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4514 @*/
4515 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4516 {
4517   DM_Plex *mesh;
4518   DMLabel  ctLabel;
4519   PetscInt pStart, pEnd, p;
4520 
4521   PetscFunctionBegin;
4522   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4523   mesh = (DM_Plex *)dm->data;
4524   PetscCall(DMCreateLabel(dm, "celltype"));
4525   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4526   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4527   PetscCall(PetscFree(mesh->cellTypes));
4528   PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
4529   for (p = pStart; p < pEnd; ++p) {
4530     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4531     PetscInt       pdepth;
4532 
4533     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4534     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4535     PetscCheck(ct != DM_POLYTOPE_UNKNOWN && ct != DM_POLYTOPE_UNKNOWN_CELL && ct != DM_POLYTOPE_UNKNOWN_FACE, PETSC_COMM_SELF, PETSC_ERR_SUP, "Point %" PetscInt_FMT " is screwed up", p);
4536     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4537     mesh->cellTypes[p - pStart].value_as_uint8 = ct;
4538   }
4539   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4540   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4541   PetscFunctionReturn(PETSC_SUCCESS);
4542 }
4543 
4544 /*@C
4545   DMPlexGetJoin - Get an array for the join of the set of points
4546 
4547   Not Collective
4548 
4549   Input Parameters:
4550 + dm        - The `DMPLEX` object
4551 . numPoints - The number of input points for the join
4552 - points    - The input points
4553 
4554   Output Parameters:
4555 + numCoveredPoints - The number of points in the join
4556 - coveredPoints    - The points in the join
4557 
4558   Level: intermediate
4559 
4560   Note:
4561   Currently, this is restricted to a single level join
4562 
4563   Fortran Notes:
4564   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4565 
4566 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4567 @*/
4568 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4569 {
4570   DM_Plex  *mesh = (DM_Plex *)dm->data;
4571   PetscInt *join[2];
4572   PetscInt  joinSize, i = 0;
4573   PetscInt  dof, off, p, c, m;
4574   PetscInt  maxSupportSize;
4575 
4576   PetscFunctionBegin;
4577   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4578   PetscAssertPointer(points, 3);
4579   PetscAssertPointer(numCoveredPoints, 4);
4580   PetscAssertPointer(coveredPoints, 5);
4581   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4582   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4583   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4584   /* Copy in support of first point */
4585   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4586   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4587   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4588   /* Check each successive support */
4589   for (p = 1; p < numPoints; ++p) {
4590     PetscInt newJoinSize = 0;
4591 
4592     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4593     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4594     for (c = 0; c < dof; ++c) {
4595       const PetscInt point = mesh->supports[off + c];
4596 
4597       for (m = 0; m < joinSize; ++m) {
4598         if (point == join[i][m]) {
4599           join[1 - i][newJoinSize++] = point;
4600           break;
4601         }
4602       }
4603     }
4604     joinSize = newJoinSize;
4605     i        = 1 - i;
4606   }
4607   *numCoveredPoints = joinSize;
4608   *coveredPoints    = join[i];
4609   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4610   PetscFunctionReturn(PETSC_SUCCESS);
4611 }
4612 
4613 /*@C
4614   DMPlexRestoreJoin - Restore an array for the join of the set of points
4615 
4616   Not Collective
4617 
4618   Input Parameters:
4619 + dm        - The `DMPLEX` object
4620 . numPoints - The number of input points for the join
4621 - points    - The input points
4622 
4623   Output Parameters:
4624 + numCoveredPoints - The number of points in the join
4625 - coveredPoints    - The points in the join
4626 
4627   Level: intermediate
4628 
4629   Fortran Notes:
4630   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4631 
4632 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4633 @*/
4634 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4635 {
4636   PetscFunctionBegin;
4637   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4638   if (points) PetscAssertPointer(points, 3);
4639   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4640   PetscAssertPointer(coveredPoints, 5);
4641   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4642   if (numCoveredPoints) *numCoveredPoints = 0;
4643   PetscFunctionReturn(PETSC_SUCCESS);
4644 }
4645 
4646 /*@C
4647   DMPlexGetFullJoin - Get an array for the join of the set of points
4648 
4649   Not Collective
4650 
4651   Input Parameters:
4652 + dm        - The `DMPLEX` object
4653 . numPoints - The number of input points for the join
4654 - points    - The input points
4655 
4656   Output Parameters:
4657 + numCoveredPoints - The number of points in the join
4658 - coveredPoints    - The points in the join
4659 
4660   Level: intermediate
4661 
4662   Fortran Notes:
4663   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4664 
4665 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4666 @*/
4667 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4668 {
4669   PetscInt *offsets, **closures;
4670   PetscInt *join[2];
4671   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4672   PetscInt  p, d, c, m, ms;
4673 
4674   PetscFunctionBegin;
4675   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4676   PetscAssertPointer(points, 3);
4677   PetscAssertPointer(numCoveredPoints, 4);
4678   PetscAssertPointer(coveredPoints, 5);
4679 
4680   PetscCall(DMPlexGetDepth(dm, &depth));
4681   PetscCall(PetscCalloc1(numPoints, &closures));
4682   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4683   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4684   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4685   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4686   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4687 
4688   for (p = 0; p < numPoints; ++p) {
4689     PetscInt closureSize;
4690 
4691     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4692 
4693     offsets[p * (depth + 2) + 0] = 0;
4694     for (d = 0; d < depth + 1; ++d) {
4695       PetscInt pStart, pEnd, i;
4696 
4697       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4698       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4699         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4700           offsets[p * (depth + 2) + d + 1] = i;
4701           break;
4702         }
4703       }
4704       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4705     }
4706     PetscCheck(offsets[p * (depth + 2) + depth + 1] == closureSize, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Total size of closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[p * (depth + 2) + depth + 1], closureSize);
4707   }
4708   for (d = 0; d < depth + 1; ++d) {
4709     PetscInt dof;
4710 
4711     /* Copy in support of first point */
4712     dof = offsets[d + 1] - offsets[d];
4713     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4714     /* Check each successive cone */
4715     for (p = 1; p < numPoints && joinSize; ++p) {
4716       PetscInt newJoinSize = 0;
4717 
4718       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
4719       for (c = 0; c < dof; ++c) {
4720         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
4721 
4722         for (m = 0; m < joinSize; ++m) {
4723           if (point == join[i][m]) {
4724             join[1 - i][newJoinSize++] = point;
4725             break;
4726           }
4727         }
4728       }
4729       joinSize = newJoinSize;
4730       i        = 1 - i;
4731     }
4732     if (joinSize) break;
4733   }
4734   *numCoveredPoints = joinSize;
4735   *coveredPoints    = join[i];
4736   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4737   PetscCall(PetscFree(closures));
4738   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4739   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
4740   PetscFunctionReturn(PETSC_SUCCESS);
4741 }
4742 
4743 /*@C
4744   DMPlexGetMeet - Get an array for the meet of the set of points
4745 
4746   Not Collective
4747 
4748   Input Parameters:
4749 + dm        - The `DMPLEX` object
4750 . numPoints - The number of input points for the meet
4751 - points    - The input points
4752 
4753   Output Parameters:
4754 + numCoveringPoints - The number of points in the meet
4755 - coveringPoints    - The points in the meet
4756 
4757   Level: intermediate
4758 
4759   Note:
4760   Currently, this is restricted to a single level meet
4761 
4762   Fortran Notes:
4763   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4764 
4765 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4766 @*/
4767 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt **coveringPoints)
4768 {
4769   DM_Plex  *mesh = (DM_Plex *)dm->data;
4770   PetscInt *meet[2];
4771   PetscInt  meetSize, i = 0;
4772   PetscInt  dof, off, p, c, m;
4773   PetscInt  maxConeSize;
4774 
4775   PetscFunctionBegin;
4776   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4777   PetscAssertPointer(points, 3);
4778   PetscAssertPointer(numCoveringPoints, 4);
4779   PetscAssertPointer(coveringPoints, 5);
4780   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4781   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4782   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4783   /* Copy in cone of first point */
4784   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4785   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4786   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
4787   /* Check each successive cone */
4788   for (p = 1; p < numPoints; ++p) {
4789     PetscInt newMeetSize = 0;
4790 
4791     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4792     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4793     for (c = 0; c < dof; ++c) {
4794       const PetscInt point = mesh->cones[off + c];
4795 
4796       for (m = 0; m < meetSize; ++m) {
4797         if (point == meet[i][m]) {
4798           meet[1 - i][newMeetSize++] = point;
4799           break;
4800         }
4801       }
4802     }
4803     meetSize = newMeetSize;
4804     i        = 1 - i;
4805   }
4806   *numCoveringPoints = meetSize;
4807   *coveringPoints    = meet[i];
4808   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
4809   PetscFunctionReturn(PETSC_SUCCESS);
4810 }
4811 
4812 /*@C
4813   DMPlexRestoreMeet - Restore an array for the meet of the set of points
4814 
4815   Not Collective
4816 
4817   Input Parameters:
4818 + dm        - The `DMPLEX` object
4819 . numPoints - The number of input points for the meet
4820 - points    - The input points
4821 
4822   Output Parameters:
4823 + numCoveredPoints - The number of points in the meet
4824 - coveredPoints    - The points in the meet
4825 
4826   Level: intermediate
4827 
4828   Fortran Notes:
4829   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4830 
4831 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
4832 @*/
4833 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4834 {
4835   PetscFunctionBegin;
4836   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4837   if (points) PetscAssertPointer(points, 3);
4838   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4839   PetscAssertPointer(coveredPoints, 5);
4840   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4841   if (numCoveredPoints) *numCoveredPoints = 0;
4842   PetscFunctionReturn(PETSC_SUCCESS);
4843 }
4844 
4845 /*@C
4846   DMPlexGetFullMeet - Get an array for the meet of the set of points
4847 
4848   Not Collective
4849 
4850   Input Parameters:
4851 + dm        - The `DMPLEX` object
4852 . numPoints - The number of input points for the meet
4853 - points    - The input points
4854 
4855   Output Parameters:
4856 + numCoveredPoints - The number of points in the meet
4857 - coveredPoints    - The points in the meet
4858 
4859   Level: intermediate
4860 
4861   Fortran Notes:
4862   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4863 
4864 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4865 @*/
4866 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4867 {
4868   PetscInt *offsets, **closures;
4869   PetscInt *meet[2];
4870   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
4871   PetscInt  p, h, c, m, mc;
4872 
4873   PetscFunctionBegin;
4874   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4875   PetscAssertPointer(points, 3);
4876   PetscAssertPointer(numCoveredPoints, 4);
4877   PetscAssertPointer(coveredPoints, 5);
4878 
4879   PetscCall(DMPlexGetDepth(dm, &height));
4880   PetscCall(PetscMalloc1(numPoints, &closures));
4881   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4882   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
4883   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
4884   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
4885   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
4886 
4887   for (p = 0; p < numPoints; ++p) {
4888     PetscInt closureSize;
4889 
4890     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
4891 
4892     offsets[p * (height + 2) + 0] = 0;
4893     for (h = 0; h < height + 1; ++h) {
4894       PetscInt pStart, pEnd, i;
4895 
4896       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
4897       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
4898         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4899           offsets[p * (height + 2) + h + 1] = i;
4900           break;
4901         }
4902       }
4903       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
4904     }
4905     PetscCheck(offsets[p * (height + 2) + height + 1] == closureSize, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Total size of closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[p * (height + 2) + height + 1], closureSize);
4906   }
4907   for (h = 0; h < height + 1; ++h) {
4908     PetscInt dof;
4909 
4910     /* Copy in cone of first point */
4911     dof = offsets[h + 1] - offsets[h];
4912     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
4913     /* Check each successive cone */
4914     for (p = 1; p < numPoints && meetSize; ++p) {
4915       PetscInt newMeetSize = 0;
4916 
4917       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
4918       for (c = 0; c < dof; ++c) {
4919         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
4920 
4921         for (m = 0; m < meetSize; ++m) {
4922           if (point == meet[i][m]) {
4923             meet[1 - i][newMeetSize++] = point;
4924             break;
4925           }
4926         }
4927       }
4928       meetSize = newMeetSize;
4929       i        = 1 - i;
4930     }
4931     if (meetSize) break;
4932   }
4933   *numCoveredPoints = meetSize;
4934   *coveredPoints    = meet[i];
4935   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
4936   PetscCall(PetscFree(closures));
4937   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4938   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
4939   PetscFunctionReturn(PETSC_SUCCESS);
4940 }
4941 
4942 /*@C
4943   DMPlexEqual - Determine if two `DM` have the same topology
4944 
4945   Not Collective
4946 
4947   Input Parameters:
4948 + dmA - A `DMPLEX` object
4949 - dmB - A `DMPLEX` object
4950 
4951   Output Parameter:
4952 . equal - `PETSC_TRUE` if the topologies are identical
4953 
4954   Level: intermediate
4955 
4956   Note:
4957   We are not solving graph isomorphism, so we do not permute.
4958 
4959 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
4960 @*/
4961 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
4962 {
4963   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
4964 
4965   PetscFunctionBegin;
4966   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
4967   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
4968   PetscAssertPointer(equal, 3);
4969 
4970   *equal = PETSC_FALSE;
4971   PetscCall(DMPlexGetDepth(dmA, &depth));
4972   PetscCall(DMPlexGetDepth(dmB, &depthB));
4973   if (depth != depthB) PetscFunctionReturn(PETSC_SUCCESS);
4974   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
4975   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
4976   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(PETSC_SUCCESS);
4977   for (p = pStart; p < pEnd; ++p) {
4978     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
4979     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
4980 
4981     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
4982     PetscCall(DMPlexGetCone(dmA, p, &cone));
4983     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
4984     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
4985     PetscCall(DMPlexGetCone(dmB, p, &coneB));
4986     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
4987     if (coneSize != coneSizeB) PetscFunctionReturn(PETSC_SUCCESS);
4988     for (c = 0; c < coneSize; ++c) {
4989       if (cone[c] != coneB[c]) PetscFunctionReturn(PETSC_SUCCESS);
4990       if (ornt[c] != orntB[c]) PetscFunctionReturn(PETSC_SUCCESS);
4991     }
4992     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
4993     PetscCall(DMPlexGetSupport(dmA, p, &support));
4994     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
4995     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
4996     if (supportSize != supportSizeB) PetscFunctionReturn(PETSC_SUCCESS);
4997     for (s = 0; s < supportSize; ++s) {
4998       if (support[s] != supportB[s]) PetscFunctionReturn(PETSC_SUCCESS);
4999     }
5000   }
5001   *equal = PETSC_TRUE;
5002   PetscFunctionReturn(PETSC_SUCCESS);
5003 }
5004 
5005 /*@C
5006   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
5007 
5008   Not Collective
5009 
5010   Input Parameters:
5011 + dm         - The `DMPLEX`
5012 . cellDim    - The cell dimension
5013 - numCorners - The number of vertices on a cell
5014 
5015   Output Parameter:
5016 . numFaceVertices - The number of vertices on a face
5017 
5018   Level: developer
5019 
5020   Note:
5021   Of course this can only work for a restricted set of symmetric shapes
5022 
5023 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5024 @*/
5025 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
5026 {
5027   MPI_Comm comm;
5028 
5029   PetscFunctionBegin;
5030   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
5031   PetscAssertPointer(numFaceVertices, 4);
5032   switch (cellDim) {
5033   case 0:
5034     *numFaceVertices = 0;
5035     break;
5036   case 1:
5037     *numFaceVertices = 1;
5038     break;
5039   case 2:
5040     switch (numCorners) {
5041     case 3:                 /* triangle */
5042       *numFaceVertices = 2; /* Edge has 2 vertices */
5043       break;
5044     case 4:                 /* quadrilateral */
5045       *numFaceVertices = 2; /* Edge has 2 vertices */
5046       break;
5047     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
5048       *numFaceVertices = 3; /* Edge has 3 vertices */
5049       break;
5050     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
5051       *numFaceVertices = 3; /* Edge has 3 vertices */
5052       break;
5053     default:
5054       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5055     }
5056     break;
5057   case 3:
5058     switch (numCorners) {
5059     case 4:                 /* tetradehdron */
5060       *numFaceVertices = 3; /* Face has 3 vertices */
5061       break;
5062     case 6:                 /* tet cohesive cells */
5063       *numFaceVertices = 4; /* Face has 4 vertices */
5064       break;
5065     case 8:                 /* hexahedron */
5066       *numFaceVertices = 4; /* Face has 4 vertices */
5067       break;
5068     case 9:                 /* tet cohesive Lagrange cells */
5069       *numFaceVertices = 6; /* Face has 6 vertices */
5070       break;
5071     case 10:                /* quadratic tetrahedron */
5072       *numFaceVertices = 6; /* Face has 6 vertices */
5073       break;
5074     case 12:                /* hex cohesive Lagrange cells */
5075       *numFaceVertices = 6; /* Face has 6 vertices */
5076       break;
5077     case 18:                /* quadratic tet cohesive Lagrange cells */
5078       *numFaceVertices = 6; /* Face has 6 vertices */
5079       break;
5080     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
5081       *numFaceVertices = 9; /* Face has 9 vertices */
5082       break;
5083     default:
5084       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5085     }
5086     break;
5087   default:
5088     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
5089   }
5090   PetscFunctionReturn(PETSC_SUCCESS);
5091 }
5092 
5093 /*@
5094   DMPlexGetDepthLabel - Get the `DMLabel` recording the depth of each point
5095 
5096   Not Collective
5097 
5098   Input Parameter:
5099 . dm - The `DMPLEX` object
5100 
5101   Output Parameter:
5102 . depthLabel - The `DMLabel` recording point depth
5103 
5104   Level: developer
5105 
5106 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
5107 @*/
5108 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
5109 {
5110   PetscFunctionBegin;
5111   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5112   PetscAssertPointer(depthLabel, 2);
5113   *depthLabel = dm->depthLabel;
5114   PetscFunctionReturn(PETSC_SUCCESS);
5115 }
5116 
5117 /*@
5118   DMPlexGetDepth - Get the depth of the DAG representing this mesh
5119 
5120   Not Collective
5121 
5122   Input Parameter:
5123 . dm - The `DMPLEX` object
5124 
5125   Output Parameter:
5126 . depth - The number of strata (breadth first levels) in the DAG
5127 
5128   Level: developer
5129 
5130   Notes:
5131   This returns maximum of point depths over all points, i.e. maximum value of the label returned by `DMPlexGetDepthLabel()`.
5132 
5133   The point depth is described more in detail in `DMPlexGetDepthStratum()`.
5134 
5135   An empty mesh gives -1.
5136 
5137 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
5138 @*/
5139 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
5140 {
5141   DM_Plex *mesh = (DM_Plex *)dm->data;
5142   DMLabel  label;
5143   PetscInt d = 0;
5144 
5145   PetscFunctionBegin;
5146   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5147   PetscAssertPointer(depth, 2);
5148   if (mesh->tr) {
5149     PetscCall(DMPlexTransformGetDepth(mesh->tr, depth));
5150   } else {
5151     PetscCall(DMPlexGetDepthLabel(dm, &label));
5152     if (label) PetscCall(DMLabelGetNumValues(label, &d));
5153     *depth = d - 1;
5154   }
5155   PetscFunctionReturn(PETSC_SUCCESS);
5156 }
5157 
5158 /*@
5159   DMPlexGetDepthStratum - Get the bounds [`start`, `end`) for all points at a certain depth.
5160 
5161   Not Collective
5162 
5163   Input Parameters:
5164 + dm    - The `DMPLEX` object
5165 - depth - The requested depth
5166 
5167   Output Parameters:
5168 + start - The first point at this `depth`
5169 - end   - One beyond the last point at this `depth`
5170 
5171   Level: developer
5172 
5173   Notes:
5174   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
5175   often "vertices".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then depth stratum 1 contains the next
5176   higher dimension, e.g., "edges".
5177 
5178 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetHeightStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
5179 @*/
5180 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
5181 {
5182   DM_Plex *mesh = (DM_Plex *)dm->data;
5183   DMLabel  label;
5184   PetscInt pStart, pEnd;
5185 
5186   PetscFunctionBegin;
5187   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5188   if (start) {
5189     PetscAssertPointer(start, 3);
5190     *start = 0;
5191   }
5192   if (end) {
5193     PetscAssertPointer(end, 4);
5194     *end = 0;
5195   }
5196   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5197   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5198   if (depth < 0) {
5199     if (start) *start = pStart;
5200     if (end) *end = pEnd;
5201     PetscFunctionReturn(PETSC_SUCCESS);
5202   }
5203   if (mesh->tr) {
5204     PetscCall(DMPlexTransformGetDepthStratum(mesh->tr, depth, start, end));
5205   } else {
5206     PetscCall(DMPlexGetDepthLabel(dm, &label));
5207     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5208     PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
5209   }
5210   PetscFunctionReturn(PETSC_SUCCESS);
5211 }
5212 
5213 /*@
5214   DMPlexGetHeightStratum - Get the bounds [`start`, `end`) for all points at a certain height.
5215 
5216   Not Collective
5217 
5218   Input Parameters:
5219 + dm     - The `DMPLEX` object
5220 - height - The requested height
5221 
5222   Output Parameters:
5223 + start - The first point at this `height`
5224 - end   - One beyond the last point at this `height`
5225 
5226   Level: developer
5227 
5228   Notes:
5229   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
5230   points, often called "cells" or "elements".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then height
5231   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
5232 
5233 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5234 @*/
5235 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
5236 {
5237   DMLabel  label;
5238   PetscInt depth, pStart, pEnd;
5239 
5240   PetscFunctionBegin;
5241   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5242   if (start) {
5243     PetscAssertPointer(start, 3);
5244     *start = 0;
5245   }
5246   if (end) {
5247     PetscAssertPointer(end, 4);
5248     *end = 0;
5249   }
5250   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5251   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5252   if (height < 0) {
5253     if (start) *start = pStart;
5254     if (end) *end = pEnd;
5255     PetscFunctionReturn(PETSC_SUCCESS);
5256   }
5257   PetscCall(DMPlexGetDepthLabel(dm, &label));
5258   if (label) PetscCall(DMLabelGetNumValues(label, &depth));
5259   else PetscCall(DMGetDimension(dm, &depth));
5260   PetscCheck(depth >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Depth not yet computed");
5261   PetscCall(DMPlexGetDepthStratum(dm, depth - 1 - height, start, end));
5262   PetscFunctionReturn(PETSC_SUCCESS);
5263 }
5264 
5265 /*@
5266   DMPlexGetPointDepth - Get the `depth` of a given point
5267 
5268   Not Collective
5269 
5270   Input Parameters:
5271 + dm    - The `DMPLEX` object
5272 - point - The point
5273 
5274   Output Parameter:
5275 . depth - The depth of the `point`
5276 
5277   Level: intermediate
5278 
5279 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5280 @*/
5281 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
5282 {
5283   PetscFunctionBegin;
5284   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5285   PetscAssertPointer(depth, 3);
5286   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
5287   PetscFunctionReturn(PETSC_SUCCESS);
5288 }
5289 
5290 /*@
5291   DMPlexGetPointHeight - Get the `height` of a given point
5292 
5293   Not Collective
5294 
5295   Input Parameters:
5296 + dm    - The `DMPLEX` object
5297 - point - The point
5298 
5299   Output Parameter:
5300 . height - The height of the `point`
5301 
5302   Level: intermediate
5303 
5304 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
5305 @*/
5306 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5307 {
5308   PetscInt n, pDepth;
5309 
5310   PetscFunctionBegin;
5311   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5312   PetscAssertPointer(height, 3);
5313   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5314   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5315   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5316   PetscFunctionReturn(PETSC_SUCCESS);
5317 }
5318 
5319 /*@
5320   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5321 
5322   Not Collective
5323 
5324   Input Parameter:
5325 . dm - The `DMPLEX` object
5326 
5327   Output Parameter:
5328 . celltypeLabel - The `DMLabel` recording cell polytope type
5329 
5330   Level: developer
5331 
5332   Note:
5333   This function will trigger automatica computation of cell types. This can be disabled by calling
5334   `DMCreateLabel`(dm, "celltype") beforehand.
5335 
5336 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5337 @*/
5338 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5339 {
5340   PetscFunctionBegin;
5341   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5342   PetscAssertPointer(celltypeLabel, 2);
5343   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5344   *celltypeLabel = dm->celltypeLabel;
5345   PetscFunctionReturn(PETSC_SUCCESS);
5346 }
5347 
5348 /*@
5349   DMPlexGetCellType - Get the polytope type of a given cell
5350 
5351   Not Collective
5352 
5353   Input Parameters:
5354 + dm   - The `DMPLEX` object
5355 - cell - The cell
5356 
5357   Output Parameter:
5358 . celltype - The polytope type of the cell
5359 
5360   Level: intermediate
5361 
5362 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeType`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5363 @*/
5364 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5365 {
5366   DM_Plex *mesh = (DM_Plex *)dm->data;
5367   DMLabel  label;
5368   PetscInt ct;
5369 
5370   PetscFunctionBegin;
5371   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5372   PetscAssertPointer(celltype, 3);
5373   if (mesh->tr) {
5374     PetscCall(DMPlexTransformGetCellType(mesh->tr, cell, celltype));
5375   } else {
5376     PetscInt pStart, pEnd;
5377 
5378     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, NULL));
5379     if (!mesh->cellTypes) { /* XXX remove? optimize? */
5380       PetscCall(PetscSectionGetChart(mesh->coneSection, NULL, &pEnd));
5381       PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5382       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5383       for (PetscInt p = pStart; p < pEnd; p++) {
5384         PetscCall(DMLabelGetValue(label, p, &ct));
5385         mesh->cellTypes[p - pStart].value_as_uint8 = (DMPolytopeType)ct;
5386       }
5387     }
5388     *celltype = (DMPolytopeType)mesh->cellTypes[cell - pStart].value_as_uint8;
5389     if (PetscDefined(USE_DEBUG)) {
5390       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5391       PetscCall(DMLabelGetValue(label, cell, &ct));
5392       PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5393       PetscCheck(ct == (PetscInt)*celltype, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid cellType for %" PetscInt_FMT ": %d != %" PetscInt_FMT, cell, (int)*celltype, ct);
5394     }
5395   }
5396   PetscFunctionReturn(PETSC_SUCCESS);
5397 }
5398 
5399 /*@
5400   DMPlexSetCellType - Set the polytope type of a given cell
5401 
5402   Not Collective
5403 
5404   Input Parameters:
5405 + dm       - The `DMPLEX` object
5406 . cell     - The cell
5407 - celltype - The polytope type of the cell
5408 
5409   Level: advanced
5410 
5411   Note:
5412   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5413   is executed. This function will override the computed type. However, if automatic classification will not succeed
5414   and a user wants to manually specify all types, the classification must be disabled by calling
5415   DMCreateLabel(dm, "celltype") before getting or setting any cell types.
5416 
5417 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5418 @*/
5419 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5420 {
5421   DM_Plex *mesh = (DM_Plex *)dm->data;
5422   DMLabel  label;
5423   PetscInt pStart, pEnd;
5424 
5425   PetscFunctionBegin;
5426   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5427   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
5428   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5429   PetscCall(DMLabelSetValue(label, cell, celltype));
5430   if (!mesh->cellTypes) PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5431   mesh->cellTypes[cell - pStart].value_as_uint8 = celltype;
5432   PetscFunctionReturn(PETSC_SUCCESS);
5433 }
5434 
5435 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5436 {
5437   PetscSection section, s;
5438   Mat          m;
5439   PetscInt     maxHeight;
5440   const char  *prefix;
5441 
5442   PetscFunctionBegin;
5443   PetscCall(DMClone(dm, cdm));
5444   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5445   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5446   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5447   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5448   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5449   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5450   PetscCall(DMSetLocalSection(*cdm, section));
5451   PetscCall(PetscSectionDestroy(&section));
5452   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &s));
5453   PetscCall(MatCreate(PETSC_COMM_SELF, &m));
5454   PetscCall(DMSetDefaultConstraints(*cdm, s, m, NULL));
5455   PetscCall(PetscSectionDestroy(&s));
5456   PetscCall(MatDestroy(&m));
5457 
5458   PetscCall(DMSetNumFields(*cdm, 1));
5459   PetscCall(DMCreateDS(*cdm));
5460   (*cdm)->cloneOpts = PETSC_TRUE;
5461   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5462   PetscFunctionReturn(PETSC_SUCCESS);
5463 }
5464 
5465 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5466 {
5467   Vec coordsLocal, cellCoordsLocal;
5468   DM  coordsDM, cellCoordsDM;
5469 
5470   PetscFunctionBegin;
5471   *field = NULL;
5472   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5473   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5474   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5475   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5476   if (coordsLocal && coordsDM) {
5477     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5478     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5479   }
5480   PetscFunctionReturn(PETSC_SUCCESS);
5481 }
5482 
5483 /*@C
5484   DMPlexGetConeSection - Return a section which describes the layout of cone data
5485 
5486   Not Collective
5487 
5488   Input Parameter:
5489 . dm - The `DMPLEX` object
5490 
5491   Output Parameter:
5492 . section - The `PetscSection` object
5493 
5494   Level: developer
5495 
5496 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5497 @*/
5498 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5499 {
5500   DM_Plex *mesh = (DM_Plex *)dm->data;
5501 
5502   PetscFunctionBegin;
5503   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5504   if (section) *section = mesh->coneSection;
5505   PetscFunctionReturn(PETSC_SUCCESS);
5506 }
5507 
5508 /*@C
5509   DMPlexGetSupportSection - Return a section which describes the layout of support data
5510 
5511   Not Collective
5512 
5513   Input Parameter:
5514 . dm - The `DMPLEX` object
5515 
5516   Output Parameter:
5517 . section - The `PetscSection` object
5518 
5519   Level: developer
5520 
5521 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5522 @*/
5523 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5524 {
5525   DM_Plex *mesh = (DM_Plex *)dm->data;
5526 
5527   PetscFunctionBegin;
5528   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5529   if (section) *section = mesh->supportSection;
5530   PetscFunctionReturn(PETSC_SUCCESS);
5531 }
5532 
5533 /*@C
5534   DMPlexGetCones - Return cone data
5535 
5536   Not Collective
5537 
5538   Input Parameter:
5539 . dm - The `DMPLEX` object
5540 
5541   Output Parameter:
5542 . cones - The cone for each point
5543 
5544   Level: developer
5545 
5546 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5547 @*/
5548 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5549 {
5550   DM_Plex *mesh = (DM_Plex *)dm->data;
5551 
5552   PetscFunctionBegin;
5553   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5554   if (cones) *cones = mesh->cones;
5555   PetscFunctionReturn(PETSC_SUCCESS);
5556 }
5557 
5558 /*@C
5559   DMPlexGetConeOrientations - Return cone orientation data
5560 
5561   Not Collective
5562 
5563   Input Parameter:
5564 . dm - The `DMPLEX` object
5565 
5566   Output Parameter:
5567 . coneOrientations - The array of cone orientations for all points
5568 
5569   Level: developer
5570 
5571   Notes:
5572   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points as returned by `DMPlexGetConeOrientation()`.
5573 
5574   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5575 
5576 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5577 @*/
5578 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5579 {
5580   DM_Plex *mesh = (DM_Plex *)dm->data;
5581 
5582   PetscFunctionBegin;
5583   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5584   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5585   PetscFunctionReturn(PETSC_SUCCESS);
5586 }
5587 
5588 /******************************** FEM Support **********************************/
5589 
5590 PetscErrorCode DMPlexGetAllCells_Internal(DM plex, IS *cellIS)
5591 {
5592   PetscInt depth;
5593 
5594   PetscFunctionBegin;
5595   PetscCall(DMPlexGetDepth(plex, &depth));
5596   PetscCall(DMGetStratumIS(plex, "dim", depth, cellIS));
5597   if (!*cellIS) PetscCall(DMGetStratumIS(plex, "depth", depth, cellIS));
5598   PetscFunctionReturn(PETSC_SUCCESS);
5599 }
5600 
5601 PetscErrorCode DMPlexGetAllFaces_Internal(DM plex, IS *faceIS)
5602 {
5603   PetscInt depth;
5604 
5605   PetscFunctionBegin;
5606   PetscCall(DMPlexGetDepth(plex, &depth));
5607   PetscCall(DMGetStratumIS(plex, "dim", depth - 1, faceIS));
5608   if (!*faceIS) PetscCall(DMGetStratumIS(plex, "depth", depth - 1, faceIS));
5609   PetscFunctionReturn(PETSC_SUCCESS);
5610 }
5611 
5612 /*
5613  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5614  representing a line in the section.
5615 */
5616 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(DM dm, PetscSection section, PetscInt field, PetscInt line, PetscInt *Nc, PetscInt *k, PetscBool *continuous)
5617 {
5618   PetscObject  obj;
5619   PetscClassId id;
5620   PetscFE      fe = NULL;
5621 
5622   PetscFunctionBeginHot;
5623   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5624   PetscCall(DMGetField(dm, field, NULL, &obj));
5625   PetscCall(PetscObjectGetClassId(obj, &id));
5626   if (id == PETSCFE_CLASSID) fe = (PetscFE)obj;
5627 
5628   if (!fe) {
5629     /* Assume the full interpolated mesh is in the chart; lines in particular */
5630     /* An order k SEM disc has k-1 dofs on an edge */
5631     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5632     *k = *k / *Nc + 1;
5633   } else {
5634     PetscInt       dual_space_size, dim;
5635     PetscDualSpace dual_space;
5636     PetscCall(DMGetDimension(dm, &dim));
5637     PetscCall(PetscFEGetDualSpace(fe, &dual_space));
5638     PetscCall(PetscDualSpaceGetDimension(dual_space, &dual_space_size));
5639     *k = (PetscInt)PetscCeilReal(PetscPowReal(dual_space_size / *Nc, 1.0 / dim)) - 1;
5640     PetscCall(PetscDualSpaceLagrangeGetContinuity(dual_space, continuous));
5641   }
5642   PetscFunctionReturn(PETSC_SUCCESS);
5643 }
5644 
5645 /*@
5646 
5647   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5648   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5649   section provided (or the section of the `DM`).
5650 
5651   Input Parameters:
5652 + dm      - The `DM`
5653 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or `PETSC_DETERMINE`
5654 - section - The `PetscSection` to reorder, or `NULL` for the default section
5655 
5656   Example:
5657   A typical interpolated single-quad mesh might order points as
5658 .vb
5659   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5660 
5661   v4 -- e6 -- v3
5662   |           |
5663   e7    c0    e8
5664   |           |
5665   v1 -- e5 -- v2
5666 .ve
5667 
5668   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5669   dofs in the order of points, e.g.,
5670 .vb
5671     c0 -> [0,1,2,3]
5672     v1 -> [4]
5673     ...
5674     e5 -> [8, 9]
5675 .ve
5676 
5677   which corresponds to the dofs
5678 .vb
5679     6   10  11  7
5680     13  2   3   15
5681     12  0   1   14
5682     4   8   9   5
5683 .ve
5684 
5685   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5686 .vb
5687   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5688 .ve
5689 
5690   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5691 .vb
5692    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5693 .ve
5694 
5695   Level: developer
5696 
5697   Notes:
5698   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5699   degree of the basis.
5700 
5701   This is required to run with libCEED.
5702 
5703 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5704 @*/
5705 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5706 {
5707   DMLabel   label;
5708   PetscInt  dim, depth = -1, eStart = -1, Nf;
5709   PetscBool continuous = PETSC_TRUE;
5710 
5711   PetscFunctionBegin;
5712   PetscCall(DMGetDimension(dm, &dim));
5713   if (dim < 1) PetscFunctionReturn(PETSC_SUCCESS);
5714   if (point < 0) {
5715     PetscInt sStart, sEnd;
5716 
5717     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5718     point = sEnd - sStart ? sStart : point;
5719   }
5720   PetscCall(DMPlexGetDepthLabel(dm, &label));
5721   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5722   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5723   if (depth == 1) {
5724     eStart = point;
5725   } else if (depth == dim) {
5726     const PetscInt *cone;
5727 
5728     PetscCall(DMPlexGetCone(dm, point, &cone));
5729     if (dim == 2) eStart = cone[0];
5730     else if (dim == 3) {
5731       const PetscInt *cone2;
5732       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5733       eStart = cone2[0];
5734     } 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);
5735   } 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);
5736 
5737   PetscCall(PetscSectionGetNumFields(section, &Nf));
5738   for (PetscInt d = 1; d <= dim; d++) {
5739     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5740     PetscInt *perm;
5741 
5742     for (f = 0; f < Nf; ++f) {
5743       PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous));
5744       size += PetscPowInt(k + 1, d) * Nc;
5745     }
5746     PetscCall(PetscMalloc1(size, &perm));
5747     for (f = 0; f < Nf; ++f) {
5748       switch (d) {
5749       case 1:
5750         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous));
5751         /*
5752          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5753          We want              [ vtx0; edge of length k-1; vtx1 ]
5754          */
5755         if (continuous) {
5756           for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
5757           for (i = 0; i < k - 1; i++)
5758             for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
5759           for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
5760           foffset = offset;
5761         } else {
5762           for (i = offset; i < size; i++) perm[i] = i - offset + foffset;
5763           foffset = offset = size;
5764         }
5765         break;
5766       case 2:
5767         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5768         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous));
5769         /* The SEM order is
5770 
5771          v_lb, {e_b}, v_rb,
5772          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5773          v_lt, reverse {e_t}, v_rt
5774          */
5775         if (continuous) {
5776           const PetscInt of   = 0;
5777           const PetscInt oeb  = of + PetscSqr(k - 1);
5778           const PetscInt oer  = oeb + (k - 1);
5779           const PetscInt oet  = oer + (k - 1);
5780           const PetscInt oel  = oet + (k - 1);
5781           const PetscInt ovlb = oel + (k - 1);
5782           const PetscInt ovrb = ovlb + 1;
5783           const PetscInt ovrt = ovrb + 1;
5784           const PetscInt ovlt = ovrt + 1;
5785           PetscInt       o;
5786 
5787           /* bottom */
5788           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
5789           for (o = oeb; o < oer; ++o)
5790             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5791           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
5792           /* middle */
5793           for (i = 0; i < k - 1; ++i) {
5794             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
5795             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
5796               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5797             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
5798           }
5799           /* top */
5800           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
5801           for (o = oel - 1; o >= oet; --o)
5802             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5803           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
5804           foffset = offset;
5805         } else {
5806           for (i = offset; i < size; i++) perm[i] = i - offset + foffset;
5807           foffset = offset = size;
5808         }
5809         break;
5810       case 3:
5811         /* The original hex closure is
5812 
5813          {c,
5814          f_b, f_t, f_f, f_b, f_r, f_l,
5815          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
5816          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
5817          */
5818         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous));
5819         /* The SEM order is
5820          Bottom Slice
5821          v_blf, {e^{(k-1)-n}_bf}, v_brf,
5822          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
5823          v_blb, {e_bb}, v_brb,
5824 
5825          Middle Slice (j)
5826          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
5827          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
5828          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
5829 
5830          Top Slice
5831          v_tlf, {e_tf}, v_trf,
5832          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
5833          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
5834          */
5835         if (continuous) {
5836           const PetscInt oc    = 0;
5837           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
5838           const PetscInt oft   = ofb + PetscSqr(k - 1);
5839           const PetscInt off   = oft + PetscSqr(k - 1);
5840           const PetscInt ofk   = off + PetscSqr(k - 1);
5841           const PetscInt ofr   = ofk + PetscSqr(k - 1);
5842           const PetscInt ofl   = ofr + PetscSqr(k - 1);
5843           const PetscInt oebl  = ofl + PetscSqr(k - 1);
5844           const PetscInt oebb  = oebl + (k - 1);
5845           const PetscInt oebr  = oebb + (k - 1);
5846           const PetscInt oebf  = oebr + (k - 1);
5847           const PetscInt oetf  = oebf + (k - 1);
5848           const PetscInt oetr  = oetf + (k - 1);
5849           const PetscInt oetb  = oetr + (k - 1);
5850           const PetscInt oetl  = oetb + (k - 1);
5851           const PetscInt oerf  = oetl + (k - 1);
5852           const PetscInt oelf  = oerf + (k - 1);
5853           const PetscInt oelb  = oelf + (k - 1);
5854           const PetscInt oerb  = oelb + (k - 1);
5855           const PetscInt ovblf = oerb + (k - 1);
5856           const PetscInt ovblb = ovblf + 1;
5857           const PetscInt ovbrb = ovblb + 1;
5858           const PetscInt ovbrf = ovbrb + 1;
5859           const PetscInt ovtlf = ovbrf + 1;
5860           const PetscInt ovtrf = ovtlf + 1;
5861           const PetscInt ovtrb = ovtrf + 1;
5862           const PetscInt ovtlb = ovtrb + 1;
5863           PetscInt       o, n;
5864 
5865           /* Bottom Slice */
5866           /*   bottom */
5867           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
5868           for (o = oetf - 1; o >= oebf; --o)
5869             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5870           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
5871           /*   middle */
5872           for (i = 0; i < k - 1; ++i) {
5873             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
5874             for (n = 0; n < k - 1; ++n) {
5875               o = ofb + n * (k - 1) + i;
5876               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5877             }
5878             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
5879           }
5880           /*   top */
5881           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
5882           for (o = oebb; o < oebr; ++o)
5883             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5884           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
5885 
5886           /* Middle Slice */
5887           for (j = 0; j < k - 1; ++j) {
5888             /*   bottom */
5889             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
5890             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
5891               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5892             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
5893             /*   middle */
5894             for (i = 0; i < k - 1; ++i) {
5895               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
5896               for (n = 0; n < k - 1; ++n)
5897                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
5898               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
5899             }
5900             /*   top */
5901             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
5902             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
5903               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5904             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
5905           }
5906 
5907           /* Top Slice */
5908           /*   bottom */
5909           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
5910           for (o = oetf; o < oetr; ++o)
5911             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5912           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
5913           /*   middle */
5914           for (i = 0; i < k - 1; ++i) {
5915             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
5916             for (n = 0; n < k - 1; ++n)
5917               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
5918             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
5919           }
5920           /*   top */
5921           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
5922           for (o = oetl - 1; o >= oetb; --o)
5923             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5924           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
5925 
5926           foffset = offset;
5927         } else {
5928           for (i = offset; i < size; i++) perm[i] = i - offset + foffset;
5929           foffset = offset = size;
5930         }
5931         break;
5932       default:
5933         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
5934       }
5935     }
5936     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
5937     /* Check permutation */
5938     {
5939       PetscInt *check;
5940 
5941       PetscCall(PetscMalloc1(size, &check));
5942       for (i = 0; i < size; ++i) {
5943         check[i] = -1;
5944         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
5945       }
5946       for (i = 0; i < size; ++i) check[perm[i]] = i;
5947       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
5948       PetscCall(PetscFree(check));
5949     }
5950     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
5951     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
5952       PetscInt *loc_perm;
5953       PetscCall(PetscMalloc1(size * 2, &loc_perm));
5954       for (PetscInt i = 0; i < size; i++) {
5955         loc_perm[i]        = perm[i];
5956         loc_perm[size + i] = size + perm[i];
5957       }
5958       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
5959     }
5960   }
5961   PetscFunctionReturn(PETSC_SUCCESS);
5962 }
5963 
5964 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
5965 {
5966   PetscDS  prob;
5967   PetscInt depth, Nf, h;
5968   DMLabel  label;
5969 
5970   PetscFunctionBeginHot;
5971   PetscCall(DMGetDS(dm, &prob));
5972   Nf      = prob->Nf;
5973   label   = dm->depthLabel;
5974   *dspace = NULL;
5975   if (field < Nf) {
5976     PetscObject disc = prob->disc[field];
5977 
5978     if (disc->classid == PETSCFE_CLASSID) {
5979       PetscDualSpace dsp;
5980 
5981       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
5982       PetscCall(DMLabelGetNumValues(label, &depth));
5983       PetscCall(DMLabelGetValue(label, point, &h));
5984       h = depth - 1 - h;
5985       if (h) {
5986         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
5987       } else {
5988         *dspace = dsp;
5989       }
5990     }
5991   }
5992   PetscFunctionReturn(PETSC_SUCCESS);
5993 }
5994 
5995 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5996 {
5997   PetscScalar       *array;
5998   const PetscScalar *vArray;
5999   const PetscInt    *cone, *coneO;
6000   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
6001 
6002   PetscFunctionBeginHot;
6003   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6004   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6005   PetscCall(DMPlexGetCone(dm, point, &cone));
6006   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6007   if (!values || !*values) {
6008     if ((point >= pStart) && (point < pEnd)) {
6009       PetscInt dof;
6010 
6011       PetscCall(PetscSectionGetDof(section, point, &dof));
6012       size += dof;
6013     }
6014     for (p = 0; p < numPoints; ++p) {
6015       const PetscInt cp = cone[p];
6016       PetscInt       dof;
6017 
6018       if ((cp < pStart) || (cp >= pEnd)) continue;
6019       PetscCall(PetscSectionGetDof(section, cp, &dof));
6020       size += dof;
6021     }
6022     if (!values) {
6023       if (csize) *csize = size;
6024       PetscFunctionReturn(PETSC_SUCCESS);
6025     }
6026     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
6027   } else {
6028     array = *values;
6029   }
6030   size = 0;
6031   PetscCall(VecGetArrayRead(v, &vArray));
6032   if ((point >= pStart) && (point < pEnd)) {
6033     PetscInt           dof, off, d;
6034     const PetscScalar *varr;
6035 
6036     PetscCall(PetscSectionGetDof(section, point, &dof));
6037     PetscCall(PetscSectionGetOffset(section, point, &off));
6038     varr = &vArray[off];
6039     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6040     size += dof;
6041   }
6042   for (p = 0; p < numPoints; ++p) {
6043     const PetscInt     cp = cone[p];
6044     PetscInt           o  = coneO[p];
6045     PetscInt           dof, off, d;
6046     const PetscScalar *varr;
6047 
6048     if ((cp < pStart) || (cp >= pEnd)) continue;
6049     PetscCall(PetscSectionGetDof(section, cp, &dof));
6050     PetscCall(PetscSectionGetOffset(section, cp, &off));
6051     varr = &vArray[off];
6052     if (o >= 0) {
6053       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6054     } else {
6055       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
6056     }
6057     size += dof;
6058   }
6059   PetscCall(VecRestoreArrayRead(v, &vArray));
6060   if (!*values) {
6061     if (csize) *csize = size;
6062     *values = array;
6063   } else {
6064     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6065     *csize = size;
6066   }
6067   PetscFunctionReturn(PETSC_SUCCESS);
6068 }
6069 
6070 /* Compress out points not in the section */
6071 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
6072 {
6073   const PetscInt np = *numPoints;
6074   PetscInt       pStart, pEnd, p, q;
6075 
6076   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6077   for (p = 0, q = 0; p < np; ++p) {
6078     const PetscInt r = points[p * 2];
6079     if ((r >= pStart) && (r < pEnd)) {
6080       points[q * 2]     = r;
6081       points[q * 2 + 1] = points[p * 2 + 1];
6082       ++q;
6083     }
6084   }
6085   *numPoints = q;
6086   return PETSC_SUCCESS;
6087 }
6088 
6089 /* Compressed closure does not apply closure permutation */
6090 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt ornt, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6091 {
6092   const PetscInt *cla = NULL;
6093   PetscInt        np, *pts = NULL;
6094 
6095   PetscFunctionBeginHot;
6096   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
6097   if (!ornt && *clPoints) {
6098     PetscInt dof, off;
6099 
6100     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
6101     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
6102     PetscCall(ISGetIndices(*clPoints, &cla));
6103     np  = dof / 2;
6104     pts = (PetscInt *)&cla[off];
6105   } else {
6106     PetscCall(DMPlexGetTransitiveClosure_Internal(dm, point, ornt, PETSC_TRUE, &np, &pts));
6107     PetscCall(CompressPoints_Private(section, &np, pts));
6108   }
6109   *numPoints = np;
6110   *points    = pts;
6111   *clp       = cla;
6112   PetscFunctionReturn(PETSC_SUCCESS);
6113 }
6114 
6115 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6116 {
6117   PetscFunctionBeginHot;
6118   if (!*clPoints) {
6119     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
6120   } else {
6121     PetscCall(ISRestoreIndices(*clPoints, clp));
6122   }
6123   *numPoints = 0;
6124   *points    = NULL;
6125   *clSec     = NULL;
6126   *clPoints  = NULL;
6127   *clp       = NULL;
6128   PetscFunctionReturn(PETSC_SUCCESS);
6129 }
6130 
6131 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
6132 {
6133   PetscInt            offset = 0, p;
6134   const PetscInt    **perms  = NULL;
6135   const PetscScalar **flips  = NULL;
6136 
6137   PetscFunctionBeginHot;
6138   *size = 0;
6139   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6140   for (p = 0; p < numPoints; p++) {
6141     const PetscInt     point = points[2 * p];
6142     const PetscInt    *perm  = perms ? perms[p] : NULL;
6143     const PetscScalar *flip  = flips ? flips[p] : NULL;
6144     PetscInt           dof, off, d;
6145     const PetscScalar *varr;
6146 
6147     PetscCall(PetscSectionGetDof(section, point, &dof));
6148     PetscCall(PetscSectionGetOffset(section, point, &off));
6149     varr = &vArray[off];
6150     if (clperm) {
6151       if (perm) {
6152         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
6153       } else {
6154         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
6155       }
6156       if (flip) {
6157         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
6158       }
6159     } else {
6160       if (perm) {
6161         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
6162       } else {
6163         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
6164       }
6165       if (flip) {
6166         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
6167       }
6168     }
6169     offset += dof;
6170   }
6171   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6172   *size = offset;
6173   PetscFunctionReturn(PETSC_SUCCESS);
6174 }
6175 
6176 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[])
6177 {
6178   PetscInt offset = 0, f;
6179 
6180   PetscFunctionBeginHot;
6181   *size = 0;
6182   for (f = 0; f < numFields; ++f) {
6183     PetscInt            p;
6184     const PetscInt    **perms = NULL;
6185     const PetscScalar **flips = NULL;
6186 
6187     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6188     for (p = 0; p < numPoints; p++) {
6189       const PetscInt     point = points[2 * p];
6190       PetscInt           fdof, foff, b;
6191       const PetscScalar *varr;
6192       const PetscInt    *perm = perms ? perms[p] : NULL;
6193       const PetscScalar *flip = flips ? flips[p] : NULL;
6194 
6195       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6196       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6197       varr = &vArray[foff];
6198       if (clperm) {
6199         if (perm) {
6200           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
6201         } else {
6202           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
6203         }
6204         if (flip) {
6205           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
6206         }
6207       } else {
6208         if (perm) {
6209           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
6210         } else {
6211           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
6212         }
6213         if (flip) {
6214           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
6215         }
6216       }
6217       offset += fdof;
6218     }
6219     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6220   }
6221   *size = offset;
6222   PetscFunctionReturn(PETSC_SUCCESS);
6223 }
6224 
6225 PetscErrorCode DMPlexVecGetOrientedClosure_Internal(DM dm, PetscSection section, PetscBool useClPerm, Vec v, PetscInt point, PetscInt ornt, PetscInt *csize, PetscScalar *values[])
6226 {
6227   PetscSection    clSection;
6228   IS              clPoints;
6229   PetscInt       *points = NULL;
6230   const PetscInt *clp, *perm = NULL;
6231   PetscInt        depth, numFields, numPoints, asize;
6232 
6233   PetscFunctionBeginHot;
6234   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6235   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6236   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6237   PetscValidHeaderSpecific(v, VEC_CLASSID, 4);
6238   PetscCall(DMPlexGetDepth(dm, &depth));
6239   PetscCall(PetscSectionGetNumFields(section, &numFields));
6240   if (depth == 1 && numFields < 2) {
6241     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6242     PetscFunctionReturn(PETSC_SUCCESS);
6243   }
6244   /* Get points */
6245   PetscCall(DMPlexGetCompressedClosure(dm, section, point, ornt, &numPoints, &points, &clSection, &clPoints, &clp));
6246   /* Get sizes */
6247   asize = 0;
6248   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6249     PetscInt dof;
6250     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6251     asize += dof;
6252   }
6253   if (values) {
6254     const PetscScalar *vArray;
6255     PetscInt           size;
6256 
6257     if (*values) {
6258       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);
6259     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6260     if (useClPerm) PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6261     PetscCall(VecGetArrayRead(v, &vArray));
6262     /* Get values */
6263     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6264     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6265     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6266     /* Cleanup array */
6267     PetscCall(VecRestoreArrayRead(v, &vArray));
6268   }
6269   if (csize) *csize = asize;
6270   /* Cleanup points */
6271   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6272   PetscFunctionReturn(PETSC_SUCCESS);
6273 }
6274 
6275 /*@C
6276   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
6277 
6278   Not collective
6279 
6280   Input Parameters:
6281 + dm      - The `DM`
6282 . section - The section describing the layout in `v`, or `NULL` to use the default section
6283 . v       - The local vector
6284 - point   - The point in the `DM`
6285 
6286   Input/Output Parameters:
6287 + csize  - The size of the input values array, or `NULL`; on output the number of values in the closure
6288 - values - An array to use for the values, or `NULL` to have it allocated automatically;
6289            if the user provided `NULL`, it is a borrowed array and should not be freed
6290 
6291   Level: intermediate
6292 
6293   Notes:
6294   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6295   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
6296   assembly function, and a user may already have allocated storage for this operation.
6297 
6298   A typical use could be
6299 .vb
6300    values = NULL;
6301    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6302    for (cl = 0; cl < clSize; ++cl) {
6303      <Compute on closure>
6304    }
6305    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
6306 .ve
6307   or
6308 .vb
6309    PetscMalloc1(clMaxSize, &values);
6310    for (p = pStart; p < pEnd; ++p) {
6311      clSize = clMaxSize;
6312      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6313      for (cl = 0; cl < clSize; ++cl) {
6314        <Compute on closure>
6315      }
6316    }
6317    PetscFree(values);
6318 .ve
6319 
6320   Fortran Notes:
6321   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6322 
6323 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6324 @*/
6325 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6326 {
6327   PetscFunctionBeginHot;
6328   PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, section, PETSC_TRUE, v, point, 0, csize, values));
6329   PetscFunctionReturn(PETSC_SUCCESS);
6330 }
6331 
6332 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6333 {
6334   DMLabel            depthLabel;
6335   PetscSection       clSection;
6336   IS                 clPoints;
6337   PetscScalar       *array;
6338   const PetscScalar *vArray;
6339   PetscInt          *points = NULL;
6340   const PetscInt    *clp, *perm = NULL;
6341   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6342 
6343   PetscFunctionBeginHot;
6344   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6345   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6346   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6347   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6348   PetscCall(DMPlexGetDepth(dm, &mdepth));
6349   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6350   PetscCall(PetscSectionGetNumFields(section, &numFields));
6351   if (mdepth == 1 && numFields < 2) {
6352     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6353     PetscFunctionReturn(PETSC_SUCCESS);
6354   }
6355   /* Get points */
6356   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6357   for (clsize = 0, p = 0; p < Np; p++) {
6358     PetscInt dof;
6359     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6360     clsize += dof;
6361   }
6362   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6363   /* Filter points */
6364   for (p = 0; p < numPoints * 2; p += 2) {
6365     PetscInt dep;
6366 
6367     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6368     if (dep != depth) continue;
6369     points[Np * 2 + 0] = points[p];
6370     points[Np * 2 + 1] = points[p + 1];
6371     ++Np;
6372   }
6373   /* Get array */
6374   if (!values || !*values) {
6375     PetscInt asize = 0, dof;
6376 
6377     for (p = 0; p < Np * 2; p += 2) {
6378       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6379       asize += dof;
6380     }
6381     if (!values) {
6382       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6383       if (csize) *csize = asize;
6384       PetscFunctionReturn(PETSC_SUCCESS);
6385     }
6386     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6387   } else {
6388     array = *values;
6389   }
6390   PetscCall(VecGetArrayRead(v, &vArray));
6391   /* Get values */
6392   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6393   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6394   /* Cleanup points */
6395   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6396   /* Cleanup array */
6397   PetscCall(VecRestoreArrayRead(v, &vArray));
6398   if (!*values) {
6399     if (csize) *csize = size;
6400     *values = array;
6401   } else {
6402     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6403     *csize = size;
6404   }
6405   PetscFunctionReturn(PETSC_SUCCESS);
6406 }
6407 
6408 /*@C
6409   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point'
6410 
6411   Not collective
6412 
6413   Input Parameters:
6414 + dm      - The `DM`
6415 . section - The section describing the layout in `v`, or `NULL` to use the default section
6416 . v       - The local vector
6417 . point   - The point in the `DM`
6418 . csize   - The number of values in the closure, or `NULL`
6419 - values  - The array of values, which is a borrowed array and should not be freed
6420 
6421   Level: intermediate
6422 
6423   Note:
6424   The array values are discarded and not copied back into `v`. In order to copy values back to `v`, use `DMPlexVecSetClosure()`
6425 
6426   Fortran Notes:
6427   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6428 
6429 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6430 @*/
6431 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6432 {
6433   PetscInt size = 0;
6434 
6435   PetscFunctionBegin;
6436   /* Should work without recalculating size */
6437   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6438   *values = NULL;
6439   PetscFunctionReturn(PETSC_SUCCESS);
6440 }
6441 
6442 static inline void add(PetscScalar *x, PetscScalar y)
6443 {
6444   *x += y;
6445 }
6446 static inline void insert(PetscScalar *x, PetscScalar y)
6447 {
6448   *x = y;
6449 }
6450 
6451 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[])
6452 {
6453   PetscInt        cdof;  /* The number of constraints on this point */
6454   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6455   PetscScalar    *a;
6456   PetscInt        off, cind = 0, k;
6457 
6458   PetscFunctionBegin;
6459   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6460   PetscCall(PetscSectionGetOffset(section, point, &off));
6461   a = &array[off];
6462   if (!cdof || setBC) {
6463     if (clperm) {
6464       if (perm) {
6465         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6466       } else {
6467         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6468       }
6469     } else {
6470       if (perm) {
6471         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6472       } else {
6473         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6474       }
6475     }
6476   } else {
6477     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6478     if (clperm) {
6479       if (perm) {
6480         for (k = 0; k < dof; ++k) {
6481           if ((cind < cdof) && (k == cdofs[cind])) {
6482             ++cind;
6483             continue;
6484           }
6485           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6486         }
6487       } else {
6488         for (k = 0; k < dof; ++k) {
6489           if ((cind < cdof) && (k == cdofs[cind])) {
6490             ++cind;
6491             continue;
6492           }
6493           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6494         }
6495       }
6496     } else {
6497       if (perm) {
6498         for (k = 0; k < dof; ++k) {
6499           if ((cind < cdof) && (k == cdofs[cind])) {
6500             ++cind;
6501             continue;
6502           }
6503           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6504         }
6505       } else {
6506         for (k = 0; k < dof; ++k) {
6507           if ((cind < cdof) && (k == cdofs[cind])) {
6508             ++cind;
6509             continue;
6510           }
6511           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6512         }
6513       }
6514     }
6515   }
6516   PetscFunctionReturn(PETSC_SUCCESS);
6517 }
6518 
6519 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[])
6520 {
6521   PetscInt        cdof;  /* The number of constraints on this point */
6522   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6523   PetscScalar    *a;
6524   PetscInt        off, cind = 0, k;
6525 
6526   PetscFunctionBegin;
6527   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6528   PetscCall(PetscSectionGetOffset(section, point, &off));
6529   a = &array[off];
6530   if (cdof) {
6531     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6532     if (clperm) {
6533       if (perm) {
6534         for (k = 0; k < dof; ++k) {
6535           if ((cind < cdof) && (k == cdofs[cind])) {
6536             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6537             cind++;
6538           }
6539         }
6540       } else {
6541         for (k = 0; k < dof; ++k) {
6542           if ((cind < cdof) && (k == cdofs[cind])) {
6543             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6544             cind++;
6545           }
6546         }
6547       }
6548     } else {
6549       if (perm) {
6550         for (k = 0; k < dof; ++k) {
6551           if ((cind < cdof) && (k == cdofs[cind])) {
6552             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6553             cind++;
6554           }
6555         }
6556       } else {
6557         for (k = 0; k < dof; ++k) {
6558           if ((cind < cdof) && (k == cdofs[cind])) {
6559             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6560             cind++;
6561           }
6562         }
6563       }
6564     }
6565   }
6566   PetscFunctionReturn(PETSC_SUCCESS);
6567 }
6568 
6569 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[])
6570 {
6571   PetscScalar    *a;
6572   PetscInt        fdof, foff, fcdof, foffset = *offset;
6573   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6574   PetscInt        cind = 0, b;
6575 
6576   PetscFunctionBegin;
6577   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6578   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6579   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6580   a = &array[foff];
6581   if (!fcdof || setBC) {
6582     if (clperm) {
6583       if (perm) {
6584         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6585       } else {
6586         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6587       }
6588     } else {
6589       if (perm) {
6590         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6591       } else {
6592         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6593       }
6594     }
6595   } else {
6596     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6597     if (clperm) {
6598       if (perm) {
6599         for (b = 0; b < fdof; b++) {
6600           if ((cind < fcdof) && (b == fcdofs[cind])) {
6601             ++cind;
6602             continue;
6603           }
6604           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6605         }
6606       } else {
6607         for (b = 0; b < fdof; b++) {
6608           if ((cind < fcdof) && (b == fcdofs[cind])) {
6609             ++cind;
6610             continue;
6611           }
6612           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6613         }
6614       }
6615     } else {
6616       if (perm) {
6617         for (b = 0; b < fdof; b++) {
6618           if ((cind < fcdof) && (b == fcdofs[cind])) {
6619             ++cind;
6620             continue;
6621           }
6622           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6623         }
6624       } else {
6625         for (b = 0; b < fdof; b++) {
6626           if ((cind < fcdof) && (b == fcdofs[cind])) {
6627             ++cind;
6628             continue;
6629           }
6630           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6631         }
6632       }
6633     }
6634   }
6635   *offset += fdof;
6636   PetscFunctionReturn(PETSC_SUCCESS);
6637 }
6638 
6639 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[])
6640 {
6641   PetscScalar    *a;
6642   PetscInt        fdof, foff, fcdof, foffset = *offset;
6643   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6644   PetscInt        Nc, cind = 0, ncind = 0, b;
6645   PetscBool       ncSet, fcSet;
6646 
6647   PetscFunctionBegin;
6648   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6649   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6650   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6651   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6652   a = &array[foff];
6653   if (fcdof) {
6654     /* We just override fcdof and fcdofs with Ncc and comps */
6655     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6656     if (clperm) {
6657       if (perm) {
6658         if (comps) {
6659           for (b = 0; b < fdof; b++) {
6660             ncSet = fcSet = PETSC_FALSE;
6661             if (b % Nc == comps[ncind]) {
6662               ncind = (ncind + 1) % Ncc;
6663               ncSet = PETSC_TRUE;
6664             }
6665             if ((cind < fcdof) && (b == fcdofs[cind])) {
6666               ++cind;
6667               fcSet = PETSC_TRUE;
6668             }
6669             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6670           }
6671         } else {
6672           for (b = 0; b < fdof; b++) {
6673             if ((cind < fcdof) && (b == fcdofs[cind])) {
6674               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6675               ++cind;
6676             }
6677           }
6678         }
6679       } else {
6680         if (comps) {
6681           for (b = 0; b < fdof; b++) {
6682             ncSet = fcSet = PETSC_FALSE;
6683             if (b % Nc == comps[ncind]) {
6684               ncind = (ncind + 1) % Ncc;
6685               ncSet = PETSC_TRUE;
6686             }
6687             if ((cind < fcdof) && (b == fcdofs[cind])) {
6688               ++cind;
6689               fcSet = PETSC_TRUE;
6690             }
6691             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6692           }
6693         } else {
6694           for (b = 0; b < fdof; b++) {
6695             if ((cind < fcdof) && (b == fcdofs[cind])) {
6696               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6697               ++cind;
6698             }
6699           }
6700         }
6701       }
6702     } else {
6703       if (perm) {
6704         if (comps) {
6705           for (b = 0; b < fdof; b++) {
6706             ncSet = fcSet = PETSC_FALSE;
6707             if (b % Nc == comps[ncind]) {
6708               ncind = (ncind + 1) % Ncc;
6709               ncSet = PETSC_TRUE;
6710             }
6711             if ((cind < fcdof) && (b == fcdofs[cind])) {
6712               ++cind;
6713               fcSet = PETSC_TRUE;
6714             }
6715             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6716           }
6717         } else {
6718           for (b = 0; b < fdof; b++) {
6719             if ((cind < fcdof) && (b == fcdofs[cind])) {
6720               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6721               ++cind;
6722             }
6723           }
6724         }
6725       } else {
6726         if (comps) {
6727           for (b = 0; b < fdof; b++) {
6728             ncSet = fcSet = PETSC_FALSE;
6729             if (b % Nc == comps[ncind]) {
6730               ncind = (ncind + 1) % Ncc;
6731               ncSet = PETSC_TRUE;
6732             }
6733             if ((cind < fcdof) && (b == fcdofs[cind])) {
6734               ++cind;
6735               fcSet = PETSC_TRUE;
6736             }
6737             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6738           }
6739         } else {
6740           for (b = 0; b < fdof; b++) {
6741             if ((cind < fcdof) && (b == fcdofs[cind])) {
6742               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6743               ++cind;
6744             }
6745           }
6746         }
6747       }
6748     }
6749   }
6750   *offset += fdof;
6751   PetscFunctionReturn(PETSC_SUCCESS);
6752 }
6753 
6754 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6755 {
6756   PetscScalar    *array;
6757   const PetscInt *cone, *coneO;
6758   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6759 
6760   PetscFunctionBeginHot;
6761   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6762   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6763   PetscCall(DMPlexGetCone(dm, point, &cone));
6764   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6765   PetscCall(VecGetArray(v, &array));
6766   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6767     const PetscInt cp = !p ? point : cone[p - 1];
6768     const PetscInt o  = !p ? 0 : coneO[p - 1];
6769 
6770     if ((cp < pStart) || (cp >= pEnd)) {
6771       dof = 0;
6772       continue;
6773     }
6774     PetscCall(PetscSectionGetDof(section, cp, &dof));
6775     /* ADD_VALUES */
6776     {
6777       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6778       PetscScalar    *a;
6779       PetscInt        cdof, coff, cind = 0, k;
6780 
6781       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
6782       PetscCall(PetscSectionGetOffset(section, cp, &coff));
6783       a = &array[coff];
6784       if (!cdof) {
6785         if (o >= 0) {
6786           for (k = 0; k < dof; ++k) a[k] += values[off + k];
6787         } else {
6788           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
6789         }
6790       } else {
6791         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
6792         if (o >= 0) {
6793           for (k = 0; k < dof; ++k) {
6794             if ((cind < cdof) && (k == cdofs[cind])) {
6795               ++cind;
6796               continue;
6797             }
6798             a[k] += values[off + k];
6799           }
6800         } else {
6801           for (k = 0; k < dof; ++k) {
6802             if ((cind < cdof) && (k == cdofs[cind])) {
6803               ++cind;
6804               continue;
6805             }
6806             a[k] += values[off + dof - k - 1];
6807           }
6808         }
6809       }
6810     }
6811   }
6812   PetscCall(VecRestoreArray(v, &array));
6813   PetscFunctionReturn(PETSC_SUCCESS);
6814 }
6815 
6816 /*@C
6817   DMPlexVecSetClosure - Set an array of the values on the closure of `point`
6818 
6819   Not collective
6820 
6821   Input Parameters:
6822 + dm      - The `DM`
6823 . section - The section describing the layout in `v`, or `NULL` to use the default section
6824 . v       - The local vector
6825 . point   - The point in the `DM`
6826 . values  - The array of values
6827 - mode    - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
6828          where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
6829 
6830   Level: intermediate
6831 
6832 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
6833 @*/
6834 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6835 {
6836   PetscSection    clSection;
6837   IS              clPoints;
6838   PetscScalar    *array;
6839   PetscInt       *points = NULL;
6840   const PetscInt *clp, *clperm = NULL;
6841   PetscInt        depth, numFields, numPoints, p, clsize;
6842 
6843   PetscFunctionBeginHot;
6844   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6845   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6846   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6847   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6848   PetscCall(DMPlexGetDepth(dm, &depth));
6849   PetscCall(PetscSectionGetNumFields(section, &numFields));
6850   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
6851     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
6852     PetscFunctionReturn(PETSC_SUCCESS);
6853   }
6854   /* Get points */
6855   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6856   for (clsize = 0, p = 0; p < numPoints; p++) {
6857     PetscInt dof;
6858     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6859     clsize += dof;
6860   }
6861   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
6862   /* Get array */
6863   PetscCall(VecGetArray(v, &array));
6864   /* Get values */
6865   if (numFields > 0) {
6866     PetscInt offset = 0, f;
6867     for (f = 0; f < numFields; ++f) {
6868       const PetscInt    **perms = NULL;
6869       const PetscScalar **flips = NULL;
6870 
6871       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6872       switch (mode) {
6873       case INSERT_VALUES:
6874         for (p = 0; p < numPoints; p++) {
6875           const PetscInt     point = points[2 * p];
6876           const PetscInt    *perm  = perms ? perms[p] : NULL;
6877           const PetscScalar *flip  = flips ? flips[p] : NULL;
6878           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array));
6879         }
6880         break;
6881       case INSERT_ALL_VALUES:
6882         for (p = 0; p < numPoints; p++) {
6883           const PetscInt     point = points[2 * p];
6884           const PetscInt    *perm  = perms ? perms[p] : NULL;
6885           const PetscScalar *flip  = flips ? flips[p] : NULL;
6886           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array));
6887         }
6888         break;
6889       case INSERT_BC_VALUES:
6890         for (p = 0; p < numPoints; p++) {
6891           const PetscInt     point = points[2 * p];
6892           const PetscInt    *perm  = perms ? perms[p] : NULL;
6893           const PetscScalar *flip  = flips ? flips[p] : NULL;
6894           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array));
6895         }
6896         break;
6897       case ADD_VALUES:
6898         for (p = 0; p < numPoints; p++) {
6899           const PetscInt     point = points[2 * p];
6900           const PetscInt    *perm  = perms ? perms[p] : NULL;
6901           const PetscScalar *flip  = flips ? flips[p] : NULL;
6902           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array));
6903         }
6904         break;
6905       case ADD_ALL_VALUES:
6906         for (p = 0; p < numPoints; p++) {
6907           const PetscInt     point = points[2 * p];
6908           const PetscInt    *perm  = perms ? perms[p] : NULL;
6909           const PetscScalar *flip  = flips ? flips[p] : NULL;
6910           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array));
6911         }
6912         break;
6913       case ADD_BC_VALUES:
6914         for (p = 0; p < numPoints; p++) {
6915           const PetscInt     point = points[2 * p];
6916           const PetscInt    *perm  = perms ? perms[p] : NULL;
6917           const PetscScalar *flip  = flips ? flips[p] : NULL;
6918           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array));
6919         }
6920         break;
6921       default:
6922         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6923       }
6924       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6925     }
6926   } else {
6927     PetscInt            dof, off;
6928     const PetscInt    **perms = NULL;
6929     const PetscScalar **flips = NULL;
6930 
6931     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6932     switch (mode) {
6933     case INSERT_VALUES:
6934       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6935         const PetscInt     point = points[2 * p];
6936         const PetscInt    *perm  = perms ? perms[p] : NULL;
6937         const PetscScalar *flip  = flips ? flips[p] : NULL;
6938         PetscCall(PetscSectionGetDof(section, point, &dof));
6939         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array));
6940       }
6941       break;
6942     case INSERT_ALL_VALUES:
6943       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6944         const PetscInt     point = points[2 * p];
6945         const PetscInt    *perm  = perms ? perms[p] : NULL;
6946         const PetscScalar *flip  = flips ? flips[p] : NULL;
6947         PetscCall(PetscSectionGetDof(section, point, &dof));
6948         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array));
6949       }
6950       break;
6951     case INSERT_BC_VALUES:
6952       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6953         const PetscInt     point = points[2 * p];
6954         const PetscInt    *perm  = perms ? perms[p] : NULL;
6955         const PetscScalar *flip  = flips ? flips[p] : NULL;
6956         PetscCall(PetscSectionGetDof(section, point, &dof));
6957         PetscCall(updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array));
6958       }
6959       break;
6960     case ADD_VALUES:
6961       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6962         const PetscInt     point = points[2 * p];
6963         const PetscInt    *perm  = perms ? perms[p] : NULL;
6964         const PetscScalar *flip  = flips ? flips[p] : NULL;
6965         PetscCall(PetscSectionGetDof(section, point, &dof));
6966         PetscCall(updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array));
6967       }
6968       break;
6969     case ADD_ALL_VALUES:
6970       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6971         const PetscInt     point = points[2 * p];
6972         const PetscInt    *perm  = perms ? perms[p] : NULL;
6973         const PetscScalar *flip  = flips ? flips[p] : NULL;
6974         PetscCall(PetscSectionGetDof(section, point, &dof));
6975         PetscCall(updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array));
6976       }
6977       break;
6978     case ADD_BC_VALUES:
6979       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6980         const PetscInt     point = points[2 * p];
6981         const PetscInt    *perm  = perms ? perms[p] : NULL;
6982         const PetscScalar *flip  = flips ? flips[p] : NULL;
6983         PetscCall(PetscSectionGetDof(section, point, &dof));
6984         PetscCall(updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array));
6985       }
6986       break;
6987     default:
6988       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6989     }
6990     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6991   }
6992   /* Cleanup points */
6993   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6994   /* Cleanup array */
6995   PetscCall(VecRestoreArray(v, &array));
6996   PetscFunctionReturn(PETSC_SUCCESS);
6997 }
6998 
6999 /* Check whether the given point is in the label. If not, update the offset to skip this point */
7000 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
7001 {
7002   PetscFunctionBegin;
7003   *contains = PETSC_TRUE;
7004   if (label) {
7005     PetscInt fdof;
7006 
7007     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
7008     if (!*contains) {
7009       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7010       *offset += fdof;
7011       PetscFunctionReturn(PETSC_SUCCESS);
7012     }
7013   }
7014   PetscFunctionReturn(PETSC_SUCCESS);
7015 }
7016 
7017 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
7018 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)
7019 {
7020   PetscSection    clSection;
7021   IS              clPoints;
7022   PetscScalar    *array;
7023   PetscInt       *points = NULL;
7024   const PetscInt *clp;
7025   PetscInt        numFields, numPoints, p;
7026   PetscInt        offset = 0, f;
7027 
7028   PetscFunctionBeginHot;
7029   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7030   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7031   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7032   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7033   PetscCall(PetscSectionGetNumFields(section, &numFields));
7034   /* Get points */
7035   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7036   /* Get array */
7037   PetscCall(VecGetArray(v, &array));
7038   /* Get values */
7039   for (f = 0; f < numFields; ++f) {
7040     const PetscInt    **perms = NULL;
7041     const PetscScalar **flips = NULL;
7042     PetscBool           contains;
7043 
7044     if (!fieldActive[f]) {
7045       for (p = 0; p < numPoints * 2; p += 2) {
7046         PetscInt fdof;
7047         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7048         offset += fdof;
7049       }
7050       continue;
7051     }
7052     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7053     switch (mode) {
7054     case INSERT_VALUES:
7055       for (p = 0; p < numPoints; p++) {
7056         const PetscInt     point = points[2 * p];
7057         const PetscInt    *perm  = perms ? perms[p] : NULL;
7058         const PetscScalar *flip  = flips ? flips[p] : NULL;
7059         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7060         if (!contains) continue;
7061         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
7062       }
7063       break;
7064     case INSERT_ALL_VALUES:
7065       for (p = 0; p < numPoints; p++) {
7066         const PetscInt     point = points[2 * p];
7067         const PetscInt    *perm  = perms ? perms[p] : NULL;
7068         const PetscScalar *flip  = flips ? flips[p] : NULL;
7069         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7070         if (!contains) continue;
7071         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
7072       }
7073       break;
7074     case INSERT_BC_VALUES:
7075       for (p = 0; p < numPoints; p++) {
7076         const PetscInt     point = points[2 * p];
7077         const PetscInt    *perm  = perms ? perms[p] : NULL;
7078         const PetscScalar *flip  = flips ? flips[p] : NULL;
7079         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7080         if (!contains) continue;
7081         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
7082       }
7083       break;
7084     case ADD_VALUES:
7085       for (p = 0; p < numPoints; p++) {
7086         const PetscInt     point = points[2 * p];
7087         const PetscInt    *perm  = perms ? perms[p] : NULL;
7088         const PetscScalar *flip  = flips ? flips[p] : NULL;
7089         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7090         if (!contains) continue;
7091         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
7092       }
7093       break;
7094     case ADD_ALL_VALUES:
7095       for (p = 0; p < numPoints; p++) {
7096         const PetscInt     point = points[2 * p];
7097         const PetscInt    *perm  = perms ? perms[p] : NULL;
7098         const PetscScalar *flip  = flips ? flips[p] : NULL;
7099         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7100         if (!contains) continue;
7101         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
7102       }
7103       break;
7104     default:
7105       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7106     }
7107     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7108   }
7109   /* Cleanup points */
7110   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7111   /* Cleanup array */
7112   PetscCall(VecRestoreArray(v, &array));
7113   PetscFunctionReturn(PETSC_SUCCESS);
7114 }
7115 
7116 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
7117 {
7118   PetscMPIInt rank;
7119   PetscInt    i, j;
7120 
7121   PetscFunctionBegin;
7122   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7123   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
7124   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
7125   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
7126   numCIndices = numCIndices ? numCIndices : numRIndices;
7127   if (!values) PetscFunctionReturn(PETSC_SUCCESS);
7128   for (i = 0; i < numRIndices; i++) {
7129     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
7130     for (j = 0; j < numCIndices; j++) {
7131 #if defined(PETSC_USE_COMPLEX)
7132       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
7133 #else
7134       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
7135 #endif
7136     }
7137     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
7138   }
7139   PetscFunctionReturn(PETSC_SUCCESS);
7140 }
7141 
7142 /*
7143   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
7144 
7145   Input Parameters:
7146 + section - The section for this data layout
7147 . islocal - Is the section (and thus indices being requested) local or global?
7148 . point   - The point contributing dofs with these indices
7149 . off     - The global offset of this point
7150 . loff    - The local offset of each field
7151 . setBC   - The flag determining whether to include indices of boundary values
7152 . perm    - A permutation of the dofs on this point, or NULL
7153 - indperm - A permutation of the entire indices array, or NULL
7154 
7155   Output Parameter:
7156 . indices - Indices for dofs on this point
7157 
7158   Level: developer
7159 
7160   Note: The indices could be local or global, depending on the value of 'off'.
7161 */
7162 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
7163 {
7164   PetscInt        dof;   /* The number of unknowns on this point */
7165   PetscInt        cdof;  /* The number of constraints on this point */
7166   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7167   PetscInt        cind = 0, k;
7168 
7169   PetscFunctionBegin;
7170   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7171   PetscCall(PetscSectionGetDof(section, point, &dof));
7172   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
7173   if (!cdof || setBC) {
7174     for (k = 0; k < dof; ++k) {
7175       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7176       const PetscInt ind    = indperm ? indperm[preind] : preind;
7177 
7178       indices[ind] = off + k;
7179     }
7180   } else {
7181     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
7182     for (k = 0; k < dof; ++k) {
7183       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7184       const PetscInt ind    = indperm ? indperm[preind] : preind;
7185 
7186       if ((cind < cdof) && (k == cdofs[cind])) {
7187         /* Insert check for returning constrained indices */
7188         indices[ind] = -(off + k + 1);
7189         ++cind;
7190       } else {
7191         indices[ind] = off + k - (islocal ? 0 : cind);
7192       }
7193     }
7194   }
7195   *loff += dof;
7196   PetscFunctionReturn(PETSC_SUCCESS);
7197 }
7198 
7199 /*
7200  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
7201 
7202  Input Parameters:
7203 + section - a section (global or local)
7204 - islocal - `PETSC_TRUE` if requesting local indices (i.e., section is local); `PETSC_FALSE` for global
7205 . point - point within section
7206 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
7207 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
7208 . setBC - identify constrained (boundary condition) points via involution.
7209 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
7210 . permsoff - offset
7211 - indperm - index permutation
7212 
7213  Output Parameter:
7214 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
7215 . indices - array to hold indices (as defined by section) of each dof associated with point
7216 
7217  Notes:
7218  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
7219  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
7220  in the local vector.
7221 
7222  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
7223  significant).  It is invalid to call with a global section and setBC=true.
7224 
7225  Developer Note:
7226  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
7227  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
7228  offset could be obtained from the section instead of passing it explicitly as we do now.
7229 
7230  Example:
7231  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
7232  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
7233  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
7234  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.
7235 
7236  Level: developer
7237 */
7238 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[])
7239 {
7240   PetscInt numFields, foff, f;
7241 
7242   PetscFunctionBegin;
7243   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7244   PetscCall(PetscSectionGetNumFields(section, &numFields));
7245   for (f = 0, foff = 0; f < numFields; ++f) {
7246     PetscInt        fdof, cfdof;
7247     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7248     PetscInt        cind = 0, b;
7249     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7250 
7251     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7252     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7253     if (!cfdof || setBC) {
7254       for (b = 0; b < fdof; ++b) {
7255         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7256         const PetscInt ind    = indperm ? indperm[preind] : preind;
7257 
7258         indices[ind] = off + foff + b;
7259       }
7260     } else {
7261       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7262       for (b = 0; b < fdof; ++b) {
7263         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7264         const PetscInt ind    = indperm ? indperm[preind] : preind;
7265 
7266         if ((cind < cfdof) && (b == fcdofs[cind])) {
7267           indices[ind] = -(off + foff + b + 1);
7268           ++cind;
7269         } else {
7270           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7271         }
7272       }
7273     }
7274     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7275     foffs[f] += fdof;
7276   }
7277   PetscFunctionReturn(PETSC_SUCCESS);
7278 }
7279 
7280 /*
7281   This version believes the globalSection offsets for each field, rather than just the point offset
7282 
7283  . foffs - The offset into 'indices' for each field, since it is segregated by field
7284 
7285  Notes:
7286  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7287  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7288 */
7289 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7290 {
7291   PetscInt numFields, foff, f;
7292 
7293   PetscFunctionBegin;
7294   PetscCall(PetscSectionGetNumFields(section, &numFields));
7295   for (f = 0; f < numFields; ++f) {
7296     PetscInt        fdof, cfdof;
7297     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7298     PetscInt        cind = 0, b;
7299     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7300 
7301     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7302     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7303     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7304     if (!cfdof) {
7305       for (b = 0; b < fdof; ++b) {
7306         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7307         const PetscInt ind    = indperm ? indperm[preind] : preind;
7308 
7309         indices[ind] = foff + b;
7310       }
7311     } else {
7312       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7313       for (b = 0; b < fdof; ++b) {
7314         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7315         const PetscInt ind    = indperm ? indperm[preind] : preind;
7316 
7317         if ((cind < cfdof) && (b == fcdofs[cind])) {
7318           indices[ind] = -(foff + b + 1);
7319           ++cind;
7320         } else {
7321           indices[ind] = foff + b - cind;
7322         }
7323       }
7324     }
7325     foffs[f] += fdof;
7326   }
7327   PetscFunctionReturn(PETSC_SUCCESS);
7328 }
7329 
7330 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)
7331 {
7332   Mat             cMat;
7333   PetscSection    aSec, cSec;
7334   IS              aIS;
7335   PetscInt        aStart = -1, aEnd = -1;
7336   const PetscInt *anchors;
7337   PetscInt        numFields, f, p, q, newP = 0;
7338   PetscInt        newNumPoints = 0, newNumIndices = 0;
7339   PetscInt       *newPoints, *indices, *newIndices;
7340   PetscInt        maxAnchor, maxDof;
7341   PetscInt        newOffsets[32];
7342   PetscInt       *pointMatOffsets[32];
7343   PetscInt       *newPointOffsets[32];
7344   PetscScalar    *pointMat[32];
7345   PetscScalar    *newValues      = NULL, *tmpValues;
7346   PetscBool       anyConstrained = PETSC_FALSE;
7347 
7348   PetscFunctionBegin;
7349   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7350   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7351   PetscCall(PetscSectionGetNumFields(section, &numFields));
7352 
7353   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7354   /* if there are point-to-point constraints */
7355   if (aSec) {
7356     PetscCall(PetscArrayzero(newOffsets, 32));
7357     PetscCall(ISGetIndices(aIS, &anchors));
7358     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7359     /* figure out how many points are going to be in the new element matrix
7360      * (we allow double counting, because it's all just going to be summed
7361      * into the global matrix anyway) */
7362     for (p = 0; p < 2 * numPoints; p += 2) {
7363       PetscInt b    = points[p];
7364       PetscInt bDof = 0, bSecDof;
7365 
7366       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7367       if (!bSecDof) continue;
7368       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7369       if (bDof) {
7370         /* this point is constrained */
7371         /* it is going to be replaced by its anchors */
7372         PetscInt bOff, q;
7373 
7374         anyConstrained = PETSC_TRUE;
7375         newNumPoints += bDof;
7376         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7377         for (q = 0; q < bDof; q++) {
7378           PetscInt a = anchors[bOff + q];
7379           PetscInt aDof;
7380 
7381           PetscCall(PetscSectionGetDof(section, a, &aDof));
7382           newNumIndices += aDof;
7383           for (f = 0; f < numFields; ++f) {
7384             PetscInt fDof;
7385 
7386             PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7387             newOffsets[f + 1] += fDof;
7388           }
7389         }
7390       } else {
7391         /* this point is not constrained */
7392         newNumPoints++;
7393         newNumIndices += bSecDof;
7394         for (f = 0; f < numFields; ++f) {
7395           PetscInt fDof;
7396 
7397           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7398           newOffsets[f + 1] += fDof;
7399         }
7400       }
7401     }
7402   }
7403   if (!anyConstrained) {
7404     if (outNumPoints) *outNumPoints = 0;
7405     if (outNumIndices) *outNumIndices = 0;
7406     if (outPoints) *outPoints = NULL;
7407     if (outValues) *outValues = NULL;
7408     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7409     PetscFunctionReturn(PETSC_SUCCESS);
7410   }
7411 
7412   if (outNumPoints) *outNumPoints = newNumPoints;
7413   if (outNumIndices) *outNumIndices = newNumIndices;
7414 
7415   for (f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7416 
7417   if (!outPoints && !outValues) {
7418     if (offsets) {
7419       for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7420     }
7421     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7422     PetscFunctionReturn(PETSC_SUCCESS);
7423   }
7424 
7425   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7426 
7427   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7428 
7429   /* workspaces */
7430   if (numFields) {
7431     for (f = 0; f < numFields; f++) {
7432       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7433       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7434     }
7435   } else {
7436     PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7437     PetscCall(DMGetWorkArray(dm, numPoints, MPIU_INT, &newPointOffsets[0]));
7438   }
7439 
7440   /* get workspaces for the point-to-point matrices */
7441   if (numFields) {
7442     PetscInt totalOffset, totalMatOffset;
7443 
7444     for (p = 0; p < numPoints; p++) {
7445       PetscInt b    = points[2 * p];
7446       PetscInt bDof = 0, bSecDof;
7447 
7448       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7449       if (!bSecDof) {
7450         for (f = 0; f < numFields; f++) {
7451           newPointOffsets[f][p + 1] = 0;
7452           pointMatOffsets[f][p + 1] = 0;
7453         }
7454         continue;
7455       }
7456       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7457       if (bDof) {
7458         for (f = 0; f < numFields; f++) {
7459           PetscInt fDof, q, bOff, allFDof = 0;
7460 
7461           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7462           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7463           for (q = 0; q < bDof; q++) {
7464             PetscInt a = anchors[bOff + q];
7465             PetscInt aFDof;
7466 
7467             PetscCall(PetscSectionGetFieldDof(section, a, f, &aFDof));
7468             allFDof += aFDof;
7469           }
7470           newPointOffsets[f][p + 1] = allFDof;
7471           pointMatOffsets[f][p + 1] = fDof * allFDof;
7472         }
7473       } else {
7474         for (f = 0; f < numFields; f++) {
7475           PetscInt fDof;
7476 
7477           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7478           newPointOffsets[f][p + 1] = fDof;
7479           pointMatOffsets[f][p + 1] = 0;
7480         }
7481       }
7482     }
7483     for (f = 0, totalOffset = 0, totalMatOffset = 0; f < numFields; f++) {
7484       newPointOffsets[f][0] = totalOffset;
7485       pointMatOffsets[f][0] = totalMatOffset;
7486       for (p = 0; p < numPoints; p++) {
7487         newPointOffsets[f][p + 1] += newPointOffsets[f][p];
7488         pointMatOffsets[f][p + 1] += pointMatOffsets[f][p];
7489       }
7490       totalOffset    = newPointOffsets[f][numPoints];
7491       totalMatOffset = pointMatOffsets[f][numPoints];
7492       PetscCall(DMGetWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7493     }
7494   } else {
7495     for (p = 0; p < numPoints; p++) {
7496       PetscInt b    = points[2 * p];
7497       PetscInt bDof = 0, bSecDof;
7498 
7499       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7500       if (!bSecDof) {
7501         newPointOffsets[0][p + 1] = 0;
7502         pointMatOffsets[0][p + 1] = 0;
7503         continue;
7504       }
7505       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7506       if (bDof) {
7507         PetscInt bOff, q, allDof = 0;
7508 
7509         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7510         for (q = 0; q < bDof; q++) {
7511           PetscInt a = anchors[bOff + q], aDof;
7512 
7513           PetscCall(PetscSectionGetDof(section, a, &aDof));
7514           allDof += aDof;
7515         }
7516         newPointOffsets[0][p + 1] = allDof;
7517         pointMatOffsets[0][p + 1] = bSecDof * allDof;
7518       } else {
7519         newPointOffsets[0][p + 1] = bSecDof;
7520         pointMatOffsets[0][p + 1] = 0;
7521       }
7522     }
7523     newPointOffsets[0][0] = 0;
7524     pointMatOffsets[0][0] = 0;
7525     for (p = 0; p < numPoints; p++) {
7526       newPointOffsets[0][p + 1] += newPointOffsets[0][p];
7527       pointMatOffsets[0][p + 1] += pointMatOffsets[0][p];
7528     }
7529     PetscCall(DMGetWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7530   }
7531 
7532   /* output arrays */
7533   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7534 
7535   /* get the point-to-point matrices; construct newPoints */
7536   PetscCall(PetscSectionGetMaxDof(aSec, &maxAnchor));
7537   PetscCall(PetscSectionGetMaxDof(section, &maxDof));
7538   PetscCall(DMGetWorkArray(dm, maxDof, MPIU_INT, &indices));
7539   PetscCall(DMGetWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7540   if (numFields) {
7541     for (p = 0, newP = 0; p < numPoints; p++) {
7542       PetscInt b    = points[2 * p];
7543       PetscInt o    = points[2 * p + 1];
7544       PetscInt bDof = 0, bSecDof;
7545 
7546       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7547       if (!bSecDof) continue;
7548       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7549       if (bDof) {
7550         PetscInt fStart[32], fEnd[32], fAnchorStart[32], fAnchorEnd[32], bOff, q;
7551 
7552         fStart[0] = 0;
7553         fEnd[0]   = 0;
7554         for (f = 0; f < numFields; f++) {
7555           PetscInt fDof;
7556 
7557           PetscCall(PetscSectionGetFieldDof(cSec, b, f, &fDof));
7558           fStart[f + 1] = fStart[f] + fDof;
7559           fEnd[f + 1]   = fStart[f + 1];
7560         }
7561         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7562         PetscCall(DMPlexGetIndicesPointFields_Internal(cSec, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, p, NULL, indices));
7563 
7564         fAnchorStart[0] = 0;
7565         fAnchorEnd[0]   = 0;
7566         for (f = 0; f < numFields; f++) {
7567           PetscInt fDof = newPointOffsets[f][p + 1] - newPointOffsets[f][p];
7568 
7569           fAnchorStart[f + 1] = fAnchorStart[f] + fDof;
7570           fAnchorEnd[f + 1]   = fAnchorStart[f + 1];
7571         }
7572         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7573         for (q = 0; q < bDof; q++) {
7574           PetscInt a = anchors[bOff + q], aOff;
7575 
7576           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7577           newPoints[2 * (newP + q)]     = a;
7578           newPoints[2 * (newP + q) + 1] = 0;
7579           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7580           PetscCall(DMPlexGetIndicesPointFields_Internal(section, PETSC_TRUE, a, aOff, fAnchorEnd, PETSC_TRUE, NULL, -1, NULL, newIndices));
7581         }
7582         newP += bDof;
7583 
7584         if (outValues) {
7585           /* get the point-to-point submatrix */
7586           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]));
7587         }
7588       } else {
7589         newPoints[2 * newP]     = b;
7590         newPoints[2 * newP + 1] = o;
7591         newP++;
7592       }
7593     }
7594   } else {
7595     for (p = 0; p < numPoints; p++) {
7596       PetscInt b    = points[2 * p];
7597       PetscInt o    = points[2 * p + 1];
7598       PetscInt bDof = 0, bSecDof;
7599 
7600       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7601       if (!bSecDof) continue;
7602       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7603       if (bDof) {
7604         PetscInt bEnd = 0, bAnchorEnd = 0, bOff;
7605 
7606         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7607         PetscCall(DMPlexGetIndicesPoint_Internal(cSec, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, indices));
7608 
7609         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7610         for (q = 0; q < bDof; q++) {
7611           PetscInt a = anchors[bOff + q], aOff;
7612 
7613           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7614 
7615           newPoints[2 * (newP + q)]     = a;
7616           newPoints[2 * (newP + q) + 1] = 0;
7617           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7618           PetscCall(DMPlexGetIndicesPoint_Internal(section, PETSC_TRUE, a, aOff, &bAnchorEnd, PETSC_TRUE, NULL, NULL, newIndices));
7619         }
7620         newP += bDof;
7621 
7622         /* get the point-to-point submatrix */
7623         if (outValues) PetscCall(MatGetValues(cMat, bEnd, indices, bAnchorEnd, newIndices, pointMat[0] + pointMatOffsets[0][p]));
7624       } else {
7625         newPoints[2 * newP]     = b;
7626         newPoints[2 * newP + 1] = o;
7627         newP++;
7628       }
7629     }
7630   }
7631 
7632   if (outValues) {
7633     PetscCall(DMGetWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7634     PetscCall(PetscArrayzero(tmpValues, newNumIndices * numIndices));
7635     /* multiply constraints on the right */
7636     if (numFields) {
7637       for (f = 0; f < numFields; f++) {
7638         PetscInt oldOff = offsets[f];
7639 
7640         for (p = 0; p < numPoints; p++) {
7641           PetscInt cStart = newPointOffsets[f][p];
7642           PetscInt b      = points[2 * p];
7643           PetscInt c, r, k;
7644           PetscInt dof;
7645 
7646           PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7647           if (!dof) continue;
7648           if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7649             PetscInt           nCols = newPointOffsets[f][p + 1] - cStart;
7650             const PetscScalar *mat   = pointMat[f] + pointMatOffsets[f][p];
7651 
7652             for (r = 0; r < numIndices; r++) {
7653               for (c = 0; c < nCols; c++) {
7654                 for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += values[r * numIndices + oldOff + k] * mat[k * nCols + c];
7655               }
7656             }
7657           } else {
7658             /* copy this column as is */
7659             for (r = 0; r < numIndices; r++) {
7660               for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7661             }
7662           }
7663           oldOff += dof;
7664         }
7665       }
7666     } else {
7667       PetscInt oldOff = 0;
7668       for (p = 0; p < numPoints; p++) {
7669         PetscInt cStart = newPointOffsets[0][p];
7670         PetscInt b      = points[2 * p];
7671         PetscInt c, r, k;
7672         PetscInt dof;
7673 
7674         PetscCall(PetscSectionGetDof(section, b, &dof));
7675         if (!dof) continue;
7676         if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7677           PetscInt           nCols = newPointOffsets[0][p + 1] - cStart;
7678           const PetscScalar *mat   = pointMat[0] + pointMatOffsets[0][p];
7679 
7680           for (r = 0; r < numIndices; r++) {
7681             for (c = 0; c < nCols; c++) {
7682               for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += mat[k * nCols + c] * values[r * numIndices + oldOff + k];
7683             }
7684           }
7685         } else {
7686           /* copy this column as is */
7687           for (r = 0; r < numIndices; r++) {
7688             for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7689           }
7690         }
7691         oldOff += dof;
7692       }
7693     }
7694 
7695     if (multiplyLeft) {
7696       PetscCall(DMGetWorkArray(dm, newNumIndices * newNumIndices, MPIU_SCALAR, &newValues));
7697       PetscCall(PetscArrayzero(newValues, newNumIndices * newNumIndices));
7698       /* multiply constraints transpose on the left */
7699       if (numFields) {
7700         for (f = 0; f < numFields; f++) {
7701           PetscInt oldOff = offsets[f];
7702 
7703           for (p = 0; p < numPoints; p++) {
7704             PetscInt rStart = newPointOffsets[f][p];
7705             PetscInt b      = points[2 * p];
7706             PetscInt c, r, k;
7707             PetscInt dof;
7708 
7709             PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7710             if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7711               PetscInt                          nRows = newPointOffsets[f][p + 1] - rStart;
7712               const PetscScalar *PETSC_RESTRICT mat   = pointMat[f] + pointMatOffsets[f][p];
7713 
7714               for (r = 0; r < nRows; r++) {
7715                 for (c = 0; c < newNumIndices; c++) {
7716                   for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7717                 }
7718               }
7719             } else {
7720               /* copy this row as is */
7721               for (r = 0; r < dof; r++) {
7722                 for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7723               }
7724             }
7725             oldOff += dof;
7726           }
7727         }
7728       } else {
7729         PetscInt oldOff = 0;
7730 
7731         for (p = 0; p < numPoints; p++) {
7732           PetscInt rStart = newPointOffsets[0][p];
7733           PetscInt b      = points[2 * p];
7734           PetscInt c, r, k;
7735           PetscInt dof;
7736 
7737           PetscCall(PetscSectionGetDof(section, b, &dof));
7738           if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7739             PetscInt                          nRows = newPointOffsets[0][p + 1] - rStart;
7740             const PetscScalar *PETSC_RESTRICT mat   = pointMat[0] + pointMatOffsets[0][p];
7741 
7742             for (r = 0; r < nRows; r++) {
7743               for (c = 0; c < newNumIndices; c++) {
7744                 for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7745               }
7746             }
7747           } else {
7748             /* copy this row as is */
7749             for (r = 0; r < dof; r++) {
7750               for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7751             }
7752           }
7753           oldOff += dof;
7754         }
7755       }
7756 
7757       PetscCall(DMRestoreWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7758     } else {
7759       newValues = tmpValues;
7760     }
7761   }
7762 
7763   /* clean up */
7764   PetscCall(DMRestoreWorkArray(dm, maxDof, MPIU_INT, &indices));
7765   PetscCall(DMRestoreWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7766 
7767   if (numFields) {
7768     for (f = 0; f < numFields; f++) {
7769       PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7770       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7771       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7772     }
7773   } else {
7774     PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7775     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7776     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[0]));
7777   }
7778   PetscCall(ISRestoreIndices(aIS, &anchors));
7779 
7780   /* output */
7781   if (outPoints) {
7782     *outPoints = newPoints;
7783   } else {
7784     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7785   }
7786   if (outValues) *outValues = newValues;
7787   for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7788   PetscFunctionReturn(PETSC_SUCCESS);
7789 }
7790 
7791 /*@C
7792   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
7793 
7794   Not collective
7795 
7796   Input Parameters:
7797 + dm         - The `DM`
7798 . section    - The `PetscSection` describing the points (a local section)
7799 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
7800 . point      - The point defining the closure
7801 - useClPerm  - Use the closure point permutation if available
7802 
7803   Output Parameters:
7804 + numIndices - The number of dof indices in the closure of point with the input sections
7805 . indices    - The dof indices
7806 . outOffsets - Array to write the field offsets into, or `NULL`
7807 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
7808 
7809   Level: advanced
7810 
7811   Notes:
7812   Must call `DMPlexRestoreClosureIndices()` to free allocated memory
7813 
7814   If `idxSection` is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
7815   of those indices is not significant.  If `idxSection` is local, the constrained dofs will yield the involution -(idx+1)
7816   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7817   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when `idxSection` == section, otherwise global
7818   indices (with the above semantics) are implied.
7819 
7820 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
7821           `PetscSection`, `DMGetGlobalSection()`
7822 @*/
7823 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7824 {
7825   /* Closure ordering */
7826   PetscSection    clSection;
7827   IS              clPoints;
7828   const PetscInt *clp;
7829   PetscInt       *points;
7830   const PetscInt *clperm = NULL;
7831   /* Dof permutation and sign flips */
7832   const PetscInt    **perms[32] = {NULL};
7833   const PetscScalar **flips[32] = {NULL};
7834   PetscScalar        *valCopy   = NULL;
7835   /* Hanging node constraints */
7836   PetscInt    *pointsC = NULL;
7837   PetscScalar *valuesC = NULL;
7838   PetscInt     NclC, NiC;
7839 
7840   PetscInt *idx;
7841   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7842   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7843 
7844   PetscFunctionBeginHot;
7845   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7846   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7847   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7848   if (numIndices) PetscAssertPointer(numIndices, 6);
7849   if (indices) PetscAssertPointer(indices, 7);
7850   if (outOffsets) PetscAssertPointer(outOffsets, 8);
7851   if (values) PetscAssertPointer(values, 9);
7852   PetscCall(PetscSectionGetNumFields(section, &Nf));
7853   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7854   PetscCall(PetscArrayzero(offsets, 32));
7855   /* 1) Get points in closure */
7856   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7857   if (useClPerm) {
7858     PetscInt depth, clsize;
7859     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7860     for (clsize = 0, p = 0; p < Ncl; p++) {
7861       PetscInt dof;
7862       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7863       clsize += dof;
7864     }
7865     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7866   }
7867   /* 2) Get number of indices on these points and field offsets from section */
7868   for (p = 0; p < Ncl * 2; p += 2) {
7869     PetscInt dof, fdof;
7870 
7871     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7872     for (f = 0; f < Nf; ++f) {
7873       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7874       offsets[f + 1] += fdof;
7875     }
7876     Ni += dof;
7877   }
7878   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
7879   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7880   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7881   for (f = 0; f < PetscMax(1, Nf); ++f) {
7882     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7883     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
7884     /* may need to apply sign changes to the element matrix */
7885     if (values && flips[f]) {
7886       PetscInt foffset = offsets[f];
7887 
7888       for (p = 0; p < Ncl; ++p) {
7889         PetscInt           pnt  = points[2 * p], fdof;
7890         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
7891 
7892         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
7893         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
7894         if (flip) {
7895           PetscInt i, j, k;
7896 
7897           if (!valCopy) {
7898             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7899             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
7900             *values = valCopy;
7901           }
7902           for (i = 0; i < fdof; ++i) {
7903             PetscScalar fval = flip[i];
7904 
7905             for (k = 0; k < Ni; ++k) {
7906               valCopy[Ni * (foffset + i) + k] *= fval;
7907               valCopy[Ni * k + (foffset + i)] *= fval;
7908             }
7909           }
7910         }
7911         foffset += fdof;
7912       }
7913     }
7914   }
7915   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
7916   PetscCall(DMPlexAnchorsModifyMat(dm, section, Ncl, Ni, points, perms, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, PETSC_TRUE));
7917   if (NclC) {
7918     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7919     for (f = 0; f < PetscMax(1, Nf); ++f) {
7920       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7921       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7922     }
7923     for (f = 0; f < PetscMax(1, Nf); ++f) {
7924       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
7925       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
7926     }
7927     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7928     Ncl    = NclC;
7929     Ni     = NiC;
7930     points = pointsC;
7931     if (values) *values = valuesC;
7932   }
7933   /* 5) Calculate indices */
7934   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
7935   if (Nf) {
7936     PetscInt  idxOff;
7937     PetscBool useFieldOffsets;
7938 
7939     if (outOffsets) {
7940       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
7941     }
7942     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
7943     if (useFieldOffsets) {
7944       for (p = 0; p < Ncl; ++p) {
7945         const PetscInt pnt = points[p * 2];
7946 
7947         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
7948       }
7949     } else {
7950       for (p = 0; p < Ncl; ++p) {
7951         const PetscInt pnt = points[p * 2];
7952 
7953         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7954         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7955          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
7956          * global section. */
7957         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
7958       }
7959     }
7960   } else {
7961     PetscInt off = 0, idxOff;
7962 
7963     for (p = 0; p < Ncl; ++p) {
7964       const PetscInt  pnt  = points[p * 2];
7965       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
7966 
7967       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7968       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7969        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
7970       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
7971     }
7972   }
7973   /* 6) Cleanup */
7974   for (f = 0; f < PetscMax(1, Nf); ++f) {
7975     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7976     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7977   }
7978   if (NclC) {
7979     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
7980   } else {
7981     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7982   }
7983 
7984   if (numIndices) *numIndices = Ni;
7985   if (indices) *indices = idx;
7986   PetscFunctionReturn(PETSC_SUCCESS);
7987 }
7988 
7989 /*@C
7990   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
7991 
7992   Not collective
7993 
7994   Input Parameters:
7995 + dm         - The `DM`
7996 . section    - The `PetscSection` describing the points (a local section)
7997 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
7998 . point      - The point defining the closure
7999 - useClPerm  - Use the closure point permutation if available
8000 
8001   Output Parameters:
8002 + numIndices - The number of dof indices in the closure of point with the input sections
8003 . indices    - The dof indices
8004 . outOffsets - Array to write the field offsets into, or `NULL`
8005 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8006 
8007   Level: advanced
8008 
8009   Notes:
8010   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
8011 
8012   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8013   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
8014   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8015   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
8016   indices (with the above semantics) are implied.
8017 
8018 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
8019 @*/
8020 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
8021 {
8022   PetscFunctionBegin;
8023   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8024   PetscAssertPointer(indices, 7);
8025   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
8026   PetscFunctionReturn(PETSC_SUCCESS);
8027 }
8028 
8029 PetscErrorCode DMPlexMatSetClosure_Internal(DM dm, PetscSection section, PetscSection globalSection, PetscBool useClPerm, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8030 {
8031   DM_Plex           *mesh = (DM_Plex *)dm->data;
8032   PetscInt          *indices;
8033   PetscInt           numIndices;
8034   const PetscScalar *valuesOrig = values;
8035   PetscErrorCode     ierr;
8036 
8037   PetscFunctionBegin;
8038   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8039   if (!section) PetscCall(DMGetLocalSection(dm, &section));
8040   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
8041   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
8042   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
8043   PetscValidHeaderSpecific(A, MAT_CLASSID, 5);
8044 
8045   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, useClPerm, &numIndices, &indices, NULL, (PetscScalar **)&values));
8046 
8047   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
8048   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8049   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
8050   if (ierr) {
8051     PetscMPIInt rank;
8052 
8053     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8054     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8055     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
8056     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8057     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8058     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
8059   }
8060   if (mesh->printFEM > 1) {
8061     PetscInt i;
8062     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
8063     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
8064     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8065   }
8066 
8067   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8068   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8069   PetscFunctionReturn(PETSC_SUCCESS);
8070 }
8071 
8072 /*@C
8073   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
8074 
8075   Not collective
8076 
8077   Input Parameters:
8078 + dm            - The `DM`
8079 . section       - The section describing the layout in `v`, or `NULL` to use the default section
8080 . globalSection - The section describing the layout in `v`, or `NULL` to use the default global section
8081 . A             - The matrix
8082 . point         - The point in the `DM`
8083 . values        - The array of values
8084 - mode          - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8085 
8086   Level: intermediate
8087 
8088 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8089 @*/
8090 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8091 {
8092   PetscFunctionBegin;
8093   PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, PETSC_TRUE, A, point, values, mode));
8094   PetscFunctionReturn(PETSC_SUCCESS);
8095 }
8096 
8097 /*@C
8098   DMPlexMatSetClosureGeneral - Set an array of the values on the closure of 'point' using a different row and column section
8099 
8100   Not collective
8101 
8102   Input Parameters:
8103 + dmRow            - The `DM` for the row fields
8104 . sectionRow       - The section describing the layout, or `NULL` to use the default section in `dmRow`
8105 . useRowPerm       - The flag to use the closure permutation of the `dmRow` if available
8106 . globalSectionRow - The section describing the layout, or `NULL` to use the default global section in `dmRow`
8107 . dmCol            - The `DM` for the column fields
8108 . sectionCol       - The section describing the layout, or `NULL` to use the default section in `dmCol`
8109 . useColPerm       - The flag to use the closure permutation of the `dmCol` if available
8110 . globalSectionCol - The section describing the layout, or `NULL` to use the default global section in `dmCol`
8111 . A                - The matrix
8112 . point            - The point in the `DM`
8113 . values           - The array of values
8114 - mode             - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8115 
8116   Level: intermediate
8117 
8118 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8119 @*/
8120 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)
8121 {
8122   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
8123   PetscInt          *indicesRow, *indicesCol;
8124   PetscInt           numIndicesRow, numIndicesCol;
8125   const PetscScalar *valuesOrig = values;
8126   PetscErrorCode     ierr;
8127 
8128   PetscFunctionBegin;
8129   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
8130   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
8131   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
8132   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
8133   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
8134   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 5);
8135   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
8136   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 6);
8137   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
8138   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 7);
8139   PetscValidHeaderSpecific(A, MAT_CLASSID, 9);
8140 
8141   PetscCall(DMPlexGetClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
8142   PetscCall(DMPlexGetClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
8143 
8144   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
8145   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8146   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values, mode);
8147   if (ierr) {
8148     PetscMPIInt rank;
8149 
8150     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8151     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8152     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
8153     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
8154     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&values));
8155     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
8156   }
8157 
8158   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
8159   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
8160   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
8161   PetscFunctionReturn(PETSC_SUCCESS);
8162 }
8163 
8164 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8165 {
8166   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
8167   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8168   PetscInt       *cpoints = NULL;
8169   PetscInt       *findices, *cindices;
8170   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8171   PetscInt        foffsets[32], coffsets[32];
8172   DMPolytopeType  ct;
8173   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8174   PetscErrorCode  ierr;
8175 
8176   PetscFunctionBegin;
8177   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8178   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8179   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8180   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8181   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8182   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8183   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8184   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8185   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8186   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8187   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
8188   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8189   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8190   PetscCall(PetscArrayzero(foffsets, 32));
8191   PetscCall(PetscArrayzero(coffsets, 32));
8192   /* Column indices */
8193   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8194   maxFPoints = numCPoints;
8195   /* Compress out points not in the section */
8196   /*   TODO: Squeeze out points with 0 dof as well */
8197   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8198   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8199     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8200       cpoints[q * 2]     = cpoints[p];
8201       cpoints[q * 2 + 1] = cpoints[p + 1];
8202       ++q;
8203     }
8204   }
8205   numCPoints = q;
8206   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8207     PetscInt fdof;
8208 
8209     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8210     if (!dof) continue;
8211     for (f = 0; f < numFields; ++f) {
8212       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8213       coffsets[f + 1] += fdof;
8214     }
8215     numCIndices += dof;
8216   }
8217   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8218   /* Row indices */
8219   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8220   {
8221     DMPlexTransform tr;
8222     DMPolytopeType *rct;
8223     PetscInt       *rsize, *rcone, *rornt, Nt;
8224 
8225     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8226     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8227     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8228     numSubcells = rsize[Nt - 1];
8229     PetscCall(DMPlexTransformDestroy(&tr));
8230   }
8231   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8232   for (r = 0, q = 0; r < numSubcells; ++r) {
8233     /* TODO Map from coarse to fine cells */
8234     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8235     /* Compress out points not in the section */
8236     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8237     for (p = 0; p < numFPoints * 2; p += 2) {
8238       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8239         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8240         if (!dof) continue;
8241         for (s = 0; s < q; ++s)
8242           if (fpoints[p] == ftotpoints[s * 2]) break;
8243         if (s < q) continue;
8244         ftotpoints[q * 2]     = fpoints[p];
8245         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8246         ++q;
8247       }
8248     }
8249     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8250   }
8251   numFPoints = q;
8252   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8253     PetscInt fdof;
8254 
8255     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8256     if (!dof) continue;
8257     for (f = 0; f < numFields; ++f) {
8258       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8259       foffsets[f + 1] += fdof;
8260     }
8261     numFIndices += dof;
8262   }
8263   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8264 
8265   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8266   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8267   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8268   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8269   if (numFields) {
8270     const PetscInt **permsF[32] = {NULL};
8271     const PetscInt **permsC[32] = {NULL};
8272 
8273     for (f = 0; f < numFields; f++) {
8274       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8275       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8276     }
8277     for (p = 0; p < numFPoints; p++) {
8278       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8279       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8280     }
8281     for (p = 0; p < numCPoints; p++) {
8282       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8283       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8284     }
8285     for (f = 0; f < numFields; f++) {
8286       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8287       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8288     }
8289   } else {
8290     const PetscInt **permsF = NULL;
8291     const PetscInt **permsC = NULL;
8292 
8293     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8294     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8295     for (p = 0, off = 0; p < numFPoints; p++) {
8296       const PetscInt *perm = permsF ? permsF[p] : NULL;
8297 
8298       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8299       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8300     }
8301     for (p = 0, off = 0; p < numCPoints; p++) {
8302       const PetscInt *perm = permsC ? permsC[p] : NULL;
8303 
8304       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8305       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8306     }
8307     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8308     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8309   }
8310   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8311   /* TODO: flips */
8312   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8313   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8314   if (ierr) {
8315     PetscMPIInt rank;
8316 
8317     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8318     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8319     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8320     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8321     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8322   }
8323   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8324   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8325   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8326   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8327   PetscFunctionReturn(PETSC_SUCCESS);
8328 }
8329 
8330 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8331 {
8332   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8333   PetscInt       *cpoints = NULL;
8334   PetscInt        foffsets[32], coffsets[32];
8335   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8336   DMPolytopeType  ct;
8337   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8338 
8339   PetscFunctionBegin;
8340   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8341   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8342   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8343   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8344   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8345   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8346   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8347   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8348   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8349   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8350   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8351   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8352   PetscCall(PetscArrayzero(foffsets, 32));
8353   PetscCall(PetscArrayzero(coffsets, 32));
8354   /* Column indices */
8355   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8356   maxFPoints = numCPoints;
8357   /* Compress out points not in the section */
8358   /*   TODO: Squeeze out points with 0 dof as well */
8359   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8360   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8361     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8362       cpoints[q * 2]     = cpoints[p];
8363       cpoints[q * 2 + 1] = cpoints[p + 1];
8364       ++q;
8365     }
8366   }
8367   numCPoints = q;
8368   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8369     PetscInt fdof;
8370 
8371     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8372     if (!dof) continue;
8373     for (f = 0; f < numFields; ++f) {
8374       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8375       coffsets[f + 1] += fdof;
8376     }
8377     numCIndices += dof;
8378   }
8379   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8380   /* Row indices */
8381   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8382   {
8383     DMPlexTransform tr;
8384     DMPolytopeType *rct;
8385     PetscInt       *rsize, *rcone, *rornt, Nt;
8386 
8387     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8388     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8389     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8390     numSubcells = rsize[Nt - 1];
8391     PetscCall(DMPlexTransformDestroy(&tr));
8392   }
8393   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8394   for (r = 0, q = 0; r < numSubcells; ++r) {
8395     /* TODO Map from coarse to fine cells */
8396     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8397     /* Compress out points not in the section */
8398     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8399     for (p = 0; p < numFPoints * 2; p += 2) {
8400       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8401         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8402         if (!dof) continue;
8403         for (s = 0; s < q; ++s)
8404           if (fpoints[p] == ftotpoints[s * 2]) break;
8405         if (s < q) continue;
8406         ftotpoints[q * 2]     = fpoints[p];
8407         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8408         ++q;
8409       }
8410     }
8411     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8412   }
8413   numFPoints = q;
8414   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8415     PetscInt fdof;
8416 
8417     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8418     if (!dof) continue;
8419     for (f = 0; f < numFields; ++f) {
8420       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8421       foffsets[f + 1] += fdof;
8422     }
8423     numFIndices += dof;
8424   }
8425   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8426 
8427   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8428   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8429   if (numFields) {
8430     const PetscInt **permsF[32] = {NULL};
8431     const PetscInt **permsC[32] = {NULL};
8432 
8433     for (f = 0; f < numFields; f++) {
8434       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8435       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8436     }
8437     for (p = 0; p < numFPoints; p++) {
8438       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8439       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8440     }
8441     for (p = 0; p < numCPoints; p++) {
8442       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8443       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8444     }
8445     for (f = 0; f < numFields; f++) {
8446       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8447       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8448     }
8449   } else {
8450     const PetscInt **permsF = NULL;
8451     const PetscInt **permsC = NULL;
8452 
8453     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8454     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8455     for (p = 0, off = 0; p < numFPoints; p++) {
8456       const PetscInt *perm = permsF ? permsF[p] : NULL;
8457 
8458       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8459       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8460     }
8461     for (p = 0, off = 0; p < numCPoints; p++) {
8462       const PetscInt *perm = permsC ? permsC[p] : NULL;
8463 
8464       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8465       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8466     }
8467     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8468     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8469   }
8470   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8471   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8472   PetscFunctionReturn(PETSC_SUCCESS);
8473 }
8474 
8475 /*@C
8476   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8477 
8478   Input Parameter:
8479 . dm - The `DMPLEX` object
8480 
8481   Output Parameter:
8482 . cellHeight - The height of a cell
8483 
8484   Level: developer
8485 
8486 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetVTKCellHeight()`
8487 @*/
8488 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8489 {
8490   DM_Plex *mesh = (DM_Plex *)dm->data;
8491 
8492   PetscFunctionBegin;
8493   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8494   PetscAssertPointer(cellHeight, 2);
8495   *cellHeight = mesh->vtkCellHeight;
8496   PetscFunctionReturn(PETSC_SUCCESS);
8497 }
8498 
8499 /*@C
8500   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8501 
8502   Input Parameters:
8503 + dm         - The `DMPLEX` object
8504 - cellHeight - The height of a cell
8505 
8506   Level: developer
8507 
8508 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8509 @*/
8510 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8511 {
8512   DM_Plex *mesh = (DM_Plex *)dm->data;
8513 
8514   PetscFunctionBegin;
8515   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8516   mesh->vtkCellHeight = cellHeight;
8517   PetscFunctionReturn(PETSC_SUCCESS);
8518 }
8519 
8520 /*@
8521   DMPlexGetCellTypeStratum - Get the range of cells of a given celltype
8522 
8523   Input Parameters:
8524 + dm - The `DMPLEX` object
8525 - ct - The `DMPolytopeType` of the cell
8526 
8527   Output Parameters:
8528 + start - The first cell of this type, or `NULL`
8529 - end   - The upper bound on this celltype, or `NULL`
8530 
8531   Level: advanced
8532 
8533 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8534 @*/
8535 PetscErrorCode DMPlexGetCellTypeStratum(DM dm, DMPolytopeType ct, PetscInt *start, PetscInt *end)
8536 {
8537   DM_Plex *mesh = (DM_Plex *)dm->data;
8538   DMLabel  label;
8539   PetscInt pStart, pEnd;
8540 
8541   PetscFunctionBegin;
8542   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8543   if (start) {
8544     PetscAssertPointer(start, 3);
8545     *start = 0;
8546   }
8547   if (end) {
8548     PetscAssertPointer(end, 4);
8549     *end = 0;
8550   }
8551   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8552   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
8553   if (mesh->tr) {
8554     PetscCall(DMPlexTransformGetCellTypeStratum(mesh->tr, ct, start, end));
8555   } else {
8556     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
8557     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named celltype was found");
8558     PetscCall(DMLabelGetStratumBounds(label, ct, start, end));
8559   }
8560   PetscFunctionReturn(PETSC_SUCCESS);
8561 }
8562 
8563 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8564 {
8565   PetscSection section, globalSection;
8566   PetscInt    *numbers, p;
8567 
8568   PetscFunctionBegin;
8569   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8570   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8571   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8572   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8573   PetscCall(PetscSectionSetUp(section));
8574   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_FALSE, &globalSection));
8575   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8576   for (p = pStart; p < pEnd; ++p) {
8577     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8578     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8579     else numbers[p - pStart] += shift;
8580   }
8581   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8582   if (globalSize) {
8583     PetscLayout layout;
8584     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8585     PetscCall(PetscLayoutGetSize(layout, globalSize));
8586     PetscCall(PetscLayoutDestroy(&layout));
8587   }
8588   PetscCall(PetscSectionDestroy(&section));
8589   PetscCall(PetscSectionDestroy(&globalSection));
8590   PetscFunctionReturn(PETSC_SUCCESS);
8591 }
8592 
8593 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
8594 {
8595   PetscInt cellHeight, cStart, cEnd;
8596 
8597   PetscFunctionBegin;
8598   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8599   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8600   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8601   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8602   PetscFunctionReturn(PETSC_SUCCESS);
8603 }
8604 
8605 /*@
8606   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8607 
8608   Input Parameter:
8609 . dm - The `DMPLEX` object
8610 
8611   Output Parameter:
8612 . globalCellNumbers - Global cell numbers for all cells on this process
8613 
8614   Level: developer
8615 
8616 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVertexNumbering()`
8617 @*/
8618 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8619 {
8620   DM_Plex *mesh = (DM_Plex *)dm->data;
8621 
8622   PetscFunctionBegin;
8623   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8624   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8625   *globalCellNumbers = mesh->globalCellNumbers;
8626   PetscFunctionReturn(PETSC_SUCCESS);
8627 }
8628 
8629 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8630 {
8631   PetscInt vStart, vEnd;
8632 
8633   PetscFunctionBegin;
8634   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8635   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8636   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8637   PetscFunctionReturn(PETSC_SUCCESS);
8638 }
8639 
8640 /*@
8641   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8642 
8643   Input Parameter:
8644 . dm - The `DMPLEX` object
8645 
8646   Output Parameter:
8647 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8648 
8649   Level: developer
8650 
8651 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8652 @*/
8653 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8654 {
8655   DM_Plex *mesh = (DM_Plex *)dm->data;
8656 
8657   PetscFunctionBegin;
8658   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8659   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8660   *globalVertexNumbers = mesh->globalVertexNumbers;
8661   PetscFunctionReturn(PETSC_SUCCESS);
8662 }
8663 
8664 /*@
8665   DMPlexCreatePointNumbering - Create a global numbering for all points.
8666 
8667   Collective
8668 
8669   Input Parameter:
8670 . dm - The `DMPLEX` object
8671 
8672   Output Parameter:
8673 . globalPointNumbers - Global numbers for all points on this process
8674 
8675   Level: developer
8676 
8677   Notes:
8678   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8679   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8680   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8681   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8682 
8683   The partitioned mesh is
8684   ```
8685   (2)--0--(3)--1--(4)    (1)--0--(2)
8686   ```
8687   and its global numbering is
8688   ```
8689   (3)--0--(4)--1--(5)--2--(6)
8690   ```
8691   Then the global numbering is provided as
8692   ```
8693   [0] Number of indices in set 5
8694   [0] 0 0
8695   [0] 1 1
8696   [0] 2 3
8697   [0] 3 4
8698   [0] 4 -6
8699   [1] Number of indices in set 3
8700   [1] 0 2
8701   [1] 1 5
8702   [1] 2 6
8703   ```
8704 
8705 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8706 @*/
8707 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8708 {
8709   IS        nums[4];
8710   PetscInt  depths[4], gdepths[4], starts[4];
8711   PetscInt  depth, d, shift = 0;
8712   PetscBool empty = PETSC_FALSE;
8713 
8714   PetscFunctionBegin;
8715   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8716   PetscCall(DMPlexGetDepth(dm, &depth));
8717   // For unstratified meshes use dim instead of depth
8718   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8719   // If any stratum is empty, we must mark all empty
8720   for (d = 0; d <= depth; ++d) {
8721     PetscInt end;
8722 
8723     depths[d] = depth - d;
8724     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8725     if (!(starts[d] - end)) empty = PETSC_TRUE;
8726   }
8727   if (empty)
8728     for (d = 0; d <= depth; ++d) {
8729       depths[d] = -1;
8730       starts[d] = -1;
8731     }
8732   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8733   PetscCall(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8734   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]);
8735   // Note here that 'shift' is collective, so that the numbering is stratified by depth
8736   for (d = 0; d <= depth; ++d) {
8737     PetscInt pStart, pEnd, gsize;
8738 
8739     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8740     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8741     shift += gsize;
8742   }
8743   PetscCall(ISConcatenate(PETSC_COMM_SELF, depth + 1, nums, globalPointNumbers));
8744   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8745   PetscFunctionReturn(PETSC_SUCCESS);
8746 }
8747 
8748 /*@
8749   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8750 
8751   Input Parameter:
8752 . dm - The `DMPLEX` object
8753 
8754   Output Parameter:
8755 . ranks - The rank field
8756 
8757   Options Database Key:
8758 . -dm_partition_view - Adds the rank field into the `DM` output from `-dm_view` using the same viewer
8759 
8760   Level: intermediate
8761 
8762 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8763 @*/
8764 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8765 {
8766   DM             rdm;
8767   PetscFE        fe;
8768   PetscScalar   *r;
8769   PetscMPIInt    rank;
8770   DMPolytopeType ct;
8771   PetscInt       dim, cStart, cEnd, c;
8772   PetscBool      simplex;
8773 
8774   PetscFunctionBeginUser;
8775   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8776   PetscAssertPointer(ranks, 2);
8777   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
8778   PetscCall(DMClone(dm, &rdm));
8779   PetscCall(DMGetDimension(rdm, &dim));
8780   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8781   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8782   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
8783   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8784   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
8785   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8786   PetscCall(PetscFEDestroy(&fe));
8787   PetscCall(DMCreateDS(rdm));
8788   PetscCall(DMCreateGlobalVector(rdm, ranks));
8789   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
8790   PetscCall(VecGetArray(*ranks, &r));
8791   for (c = cStart; c < cEnd; ++c) {
8792     PetscScalar *lr;
8793 
8794     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8795     if (lr) *lr = rank;
8796   }
8797   PetscCall(VecRestoreArray(*ranks, &r));
8798   PetscCall(DMDestroy(&rdm));
8799   PetscFunctionReturn(PETSC_SUCCESS);
8800 }
8801 
8802 /*@
8803   DMPlexCreateLabelField - Create a cell field whose value is the label value for that cell
8804 
8805   Input Parameters:
8806 + dm    - The `DMPLEX`
8807 - label - The `DMLabel`
8808 
8809   Output Parameter:
8810 . val - The label value field
8811 
8812   Options Database Key:
8813 . -dm_label_view - Adds the label value field into the `DM` output from `-dm_view` using the same viewer
8814 
8815   Level: intermediate
8816 
8817 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8818 @*/
8819 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
8820 {
8821   DM           rdm;
8822   PetscFE      fe;
8823   PetscScalar *v;
8824   PetscInt     dim, cStart, cEnd, c;
8825 
8826   PetscFunctionBeginUser;
8827   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8828   PetscAssertPointer(label, 2);
8829   PetscAssertPointer(val, 3);
8830   PetscCall(DMClone(dm, &rdm));
8831   PetscCall(DMGetDimension(rdm, &dim));
8832   PetscCall(PetscFECreateDefault(PetscObjectComm((PetscObject)rdm), dim, 1, PETSC_TRUE, "PETSc___label_value_", -1, &fe));
8833   PetscCall(PetscObjectSetName((PetscObject)fe, "label_value"));
8834   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8835   PetscCall(PetscFEDestroy(&fe));
8836   PetscCall(DMCreateDS(rdm));
8837   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8838   PetscCall(DMCreateGlobalVector(rdm, val));
8839   PetscCall(PetscObjectSetName((PetscObject)*val, "label_value"));
8840   PetscCall(VecGetArray(*val, &v));
8841   for (c = cStart; c < cEnd; ++c) {
8842     PetscScalar *lv;
8843     PetscInt     cval;
8844 
8845     PetscCall(DMPlexPointGlobalRef(rdm, c, v, &lv));
8846     PetscCall(DMLabelGetValue(label, c, &cval));
8847     *lv = cval;
8848   }
8849   PetscCall(VecRestoreArray(*val, &v));
8850   PetscCall(DMDestroy(&rdm));
8851   PetscFunctionReturn(PETSC_SUCCESS);
8852 }
8853 
8854 /*@
8855   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
8856 
8857   Input Parameter:
8858 . dm - The `DMPLEX` object
8859 
8860   Level: developer
8861 
8862   Notes:
8863   This is a useful diagnostic when creating meshes programmatically.
8864 
8865   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8866 
8867 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8868 @*/
8869 PetscErrorCode DMPlexCheckSymmetry(DM dm)
8870 {
8871   PetscSection    coneSection, supportSection;
8872   const PetscInt *cone, *support;
8873   PetscInt        coneSize, c, supportSize, s;
8874   PetscInt        pStart, pEnd, p, pp, csize, ssize;
8875   PetscBool       storagecheck = PETSC_TRUE;
8876 
8877   PetscFunctionBegin;
8878   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8879   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
8880   PetscCall(DMPlexGetConeSection(dm, &coneSection));
8881   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
8882   /* Check that point p is found in the support of its cone points, and vice versa */
8883   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8884   for (p = pStart; p < pEnd; ++p) {
8885     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
8886     PetscCall(DMPlexGetCone(dm, p, &cone));
8887     for (c = 0; c < coneSize; ++c) {
8888       PetscBool dup = PETSC_FALSE;
8889       PetscInt  d;
8890       for (d = c - 1; d >= 0; --d) {
8891         if (cone[c] == cone[d]) {
8892           dup = PETSC_TRUE;
8893           break;
8894         }
8895       }
8896       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
8897       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
8898       for (s = 0; s < supportSize; ++s) {
8899         if (support[s] == p) break;
8900       }
8901       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
8902         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
8903         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
8904         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8905         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
8906         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
8907         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8908         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]);
8909         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
8910       }
8911     }
8912     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
8913     if (p != pp) {
8914       storagecheck = PETSC_FALSE;
8915       continue;
8916     }
8917     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
8918     PetscCall(DMPlexGetSupport(dm, p, &support));
8919     for (s = 0; s < supportSize; ++s) {
8920       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
8921       PetscCall(DMPlexGetCone(dm, support[s], &cone));
8922       for (c = 0; c < coneSize; ++c) {
8923         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
8924         if (cone[c] != pp) {
8925           c = 0;
8926           break;
8927         }
8928         if (cone[c] == p) break;
8929       }
8930       if (c >= coneSize) {
8931         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
8932         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
8933         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8934         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
8935         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
8936         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8937         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
8938       }
8939     }
8940   }
8941   if (storagecheck) {
8942     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
8943     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
8944     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
8945   }
8946   PetscFunctionReturn(PETSC_SUCCESS);
8947 }
8948 
8949 /*
8950   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.
8951 */
8952 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
8953 {
8954   DMPolytopeType  cct;
8955   PetscInt        ptpoints[4];
8956   const PetscInt *cone, *ccone, *ptcone;
8957   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
8958 
8959   PetscFunctionBegin;
8960   *unsplit = 0;
8961   switch (ct) {
8962   case DM_POLYTOPE_POINT_PRISM_TENSOR:
8963     ptpoints[npt++] = c;
8964     break;
8965   case DM_POLYTOPE_SEG_PRISM_TENSOR:
8966     PetscCall(DMPlexGetCone(dm, c, &cone));
8967     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8968     for (cp = 0; cp < coneSize; ++cp) {
8969       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
8970       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
8971     }
8972     break;
8973   case DM_POLYTOPE_TRI_PRISM_TENSOR:
8974   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8975     PetscCall(DMPlexGetCone(dm, c, &cone));
8976     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8977     for (cp = 0; cp < coneSize; ++cp) {
8978       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
8979       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
8980       for (ccp = 0; ccp < cconeSize; ++ccp) {
8981         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
8982         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
8983           PetscInt p;
8984           for (p = 0; p < npt; ++p)
8985             if (ptpoints[p] == ccone[ccp]) break;
8986           if (p == npt) ptpoints[npt++] = ccone[ccp];
8987         }
8988       }
8989     }
8990     break;
8991   default:
8992     break;
8993   }
8994   for (pt = 0; pt < npt; ++pt) {
8995     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
8996     if (ptcone[0] == ptcone[1]) ++(*unsplit);
8997   }
8998   PetscFunctionReturn(PETSC_SUCCESS);
8999 }
9000 
9001 /*@
9002   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
9003 
9004   Input Parameters:
9005 + dm         - The `DMPLEX` object
9006 - cellHeight - Normally 0
9007 
9008   Level: developer
9009 
9010   Notes:
9011   This is a useful diagnostic when creating meshes programmatically.
9012   Currently applicable only to homogeneous simplex or tensor meshes.
9013 
9014   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9015 
9016 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9017 @*/
9018 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
9019 {
9020   DMPlexInterpolatedFlag interp;
9021   DMPolytopeType         ct;
9022   PetscInt               vStart, vEnd, cStart, cEnd, c;
9023 
9024   PetscFunctionBegin;
9025   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9026   PetscCall(DMPlexIsInterpolated(dm, &interp));
9027   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9028   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9029   for (c = cStart; c < cEnd; ++c) {
9030     PetscInt *closure = NULL;
9031     PetscInt  coneSize, closureSize, cl, Nv = 0;
9032 
9033     PetscCall(DMPlexGetCellType(dm, c, &ct));
9034     PetscCheck((PetscInt)ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
9035     if (ct == DM_POLYTOPE_UNKNOWN) continue;
9036     if (interp == DMPLEX_INTERPOLATED_FULL) {
9037       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9038       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));
9039     }
9040     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9041     for (cl = 0; cl < closureSize * 2; cl += 2) {
9042       const PetscInt p = closure[cl];
9043       if ((p >= vStart) && (p < vEnd)) ++Nv;
9044     }
9045     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9046     /* Special Case: Tensor faces with identified vertices */
9047     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
9048       PetscInt unsplit;
9049 
9050       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9051       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
9052     }
9053     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));
9054   }
9055   PetscFunctionReturn(PETSC_SUCCESS);
9056 }
9057 
9058 /*@
9059   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
9060 
9061   Collective
9062 
9063   Input Parameters:
9064 + dm         - The `DMPLEX` object
9065 - cellHeight - Normally 0
9066 
9067   Level: developer
9068 
9069   Notes:
9070   This is a useful diagnostic when creating meshes programmatically.
9071   This routine is only relevant for meshes that are fully interpolated across all ranks.
9072   It will error out if a partially interpolated mesh is given on some rank.
9073   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
9074 
9075   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9076 
9077 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
9078 @*/
9079 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
9080 {
9081   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
9082   DMPlexInterpolatedFlag interpEnum;
9083 
9084   PetscFunctionBegin;
9085   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9086   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
9087   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(PETSC_SUCCESS);
9088   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
9089     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported"));
9090     PetscFunctionReturn(PETSC_SUCCESS);
9091   }
9092 
9093   PetscCall(DMGetDimension(dm, &dim));
9094   PetscCall(DMPlexGetDepth(dm, &depth));
9095   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9096   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
9097     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
9098     for (c = cStart; c < cEnd; ++c) {
9099       const PetscInt       *cone, *ornt, *faceSizes, *faces;
9100       const DMPolytopeType *faceTypes;
9101       DMPolytopeType        ct;
9102       PetscInt              numFaces, coneSize, f;
9103       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
9104 
9105       PetscCall(DMPlexGetCellType(dm, c, &ct));
9106       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9107       if (unsplit) continue;
9108       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9109       PetscCall(DMPlexGetCone(dm, c, &cone));
9110       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
9111       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9112       for (cl = 0; cl < closureSize * 2; cl += 2) {
9113         const PetscInt p = closure[cl];
9114         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
9115       }
9116       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9117       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);
9118       for (f = 0; f < numFaces; ++f) {
9119         DMPolytopeType fct;
9120         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
9121 
9122         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
9123         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
9124         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
9125           const PetscInt p = fclosure[cl];
9126           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
9127         }
9128         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]);
9129         for (v = 0; v < fnumCorners; ++v) {
9130           if (fclosure[v] != faces[fOff + v]) {
9131             PetscInt v1;
9132 
9133             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
9134             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
9135             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
9136             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
9137             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9138             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]);
9139           }
9140         }
9141         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
9142         fOff += faceSizes[f];
9143       }
9144       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9145       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9146     }
9147   }
9148   PetscFunctionReturn(PETSC_SUCCESS);
9149 }
9150 
9151 /*@
9152   DMPlexCheckGeometry - Check the geometry of mesh cells
9153 
9154   Input Parameter:
9155 . dm - The `DMPLEX` object
9156 
9157   Level: developer
9158 
9159   Notes:
9160   This is a useful diagnostic when creating meshes programmatically.
9161 
9162   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9163 
9164 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9165 @*/
9166 PetscErrorCode DMPlexCheckGeometry(DM dm)
9167 {
9168   Vec       coordinates;
9169   PetscReal detJ, J[9], refVol = 1.0;
9170   PetscReal vol;
9171   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
9172 
9173   PetscFunctionBegin;
9174   PetscCall(DMGetDimension(dm, &dim));
9175   PetscCall(DMGetCoordinateDim(dm, &dE));
9176   if (dim != dE) PetscFunctionReturn(PETSC_SUCCESS);
9177   PetscCall(DMPlexGetDepth(dm, &depth));
9178   for (d = 0; d < dim; ++d) refVol *= 2.0;
9179   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9180   /* Make sure local coordinates are created, because that step is collective */
9181   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
9182   if (!coordinates) PetscFunctionReturn(PETSC_SUCCESS);
9183   for (c = cStart; c < cEnd; ++c) {
9184     DMPolytopeType ct;
9185     PetscInt       unsplit;
9186     PetscBool      ignoreZeroVol = PETSC_FALSE;
9187 
9188     PetscCall(DMPlexGetCellType(dm, c, &ct));
9189     switch (ct) {
9190     case DM_POLYTOPE_SEG_PRISM_TENSOR:
9191     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9192     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9193       ignoreZeroVol = PETSC_TRUE;
9194       break;
9195     default:
9196       break;
9197     }
9198     switch (ct) {
9199     case DM_POLYTOPE_TRI_PRISM:
9200     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9201     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9202     case DM_POLYTOPE_PYRAMID:
9203       continue;
9204     default:
9205       break;
9206     }
9207     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9208     if (unsplit) continue;
9209     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
9210     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);
9211     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
9212     /* This should work with periodicity since DG coordinates should be used */
9213     if (depth > 1) {
9214       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
9215       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);
9216       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
9217     }
9218   }
9219   PetscFunctionReturn(PETSC_SUCCESS);
9220 }
9221 
9222 /*@
9223   DMPlexCheckPointSF - Check that several necessary conditions are met for the point `PetscSF` of this plex.
9224 
9225   Collective
9226 
9227   Input Parameters:
9228 + dm              - The `DMPLEX` object
9229 . pointSF         - The `PetscSF`, or `NULL` for `PointSF` attached to `DM`
9230 - allowExtraRoots - Flag to allow extra points not present in the `DM`
9231 
9232   Level: developer
9233 
9234   Notes:
9235   This is mainly intended for debugging/testing purposes.
9236 
9237   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9238 
9239   Extra roots can come from periodic cuts, where additional points appear on the boundary
9240 
9241 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
9242 @*/
9243 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
9244 {
9245   PetscInt           l, nleaves, nroots, overlap;
9246   const PetscInt    *locals;
9247   const PetscSFNode *remotes;
9248   PetscBool          distributed;
9249   MPI_Comm           comm;
9250   PetscMPIInt        rank;
9251 
9252   PetscFunctionBegin;
9253   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9254   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
9255   else pointSF = dm->sf;
9256   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9257   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
9258   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9259   {
9260     PetscMPIInt mpiFlag;
9261 
9262     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
9263     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
9264   }
9265   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
9266   PetscCall(DMPlexIsDistributed(dm, &distributed));
9267   if (!distributed) {
9268     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);
9269     PetscFunctionReturn(PETSC_SUCCESS);
9270   }
9271   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);
9272   PetscCall(DMPlexGetOverlap(dm, &overlap));
9273 
9274   /* Check SF graph is compatible with DMPlex chart */
9275   {
9276     PetscInt pStart, pEnd, maxLeaf;
9277 
9278     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9279     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9280     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9281     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9282   }
9283 
9284   /* Check Point SF has no local points referenced */
9285   for (l = 0; l < nleaves; l++) {
9286     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);
9287   }
9288 
9289   /* Check there are no cells in interface */
9290   if (!overlap) {
9291     PetscInt cellHeight, cStart, cEnd;
9292 
9293     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9294     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9295     for (l = 0; l < nleaves; ++l) {
9296       const PetscInt point = locals ? locals[l] : l;
9297 
9298       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9299     }
9300   }
9301 
9302   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9303   {
9304     const PetscInt *rootdegree;
9305 
9306     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9307     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9308     for (l = 0; l < nleaves; ++l) {
9309       const PetscInt  point = locals ? locals[l] : l;
9310       const PetscInt *cone;
9311       PetscInt        coneSize, c, idx;
9312 
9313       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9314       PetscCall(DMPlexGetCone(dm, point, &cone));
9315       for (c = 0; c < coneSize; ++c) {
9316         if (!rootdegree[cone[c]]) {
9317           if (locals) {
9318             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9319           } else {
9320             idx = (cone[c] < nleaves) ? cone[c] : -1;
9321           }
9322           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9323         }
9324       }
9325     }
9326   }
9327   PetscFunctionReturn(PETSC_SUCCESS);
9328 }
9329 
9330 /*@
9331   DMPlexCheck - Perform various checks of `DMPLEX` sanity
9332 
9333   Input Parameter:
9334 . dm - The `DMPLEX` object
9335 
9336   Level: developer
9337 
9338   Notes:
9339   This is a useful diagnostic when creating meshes programmatically.
9340 
9341   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9342 
9343   Currently does not include `DMPlexCheckCellShape()`.
9344 
9345 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9346 @*/
9347 PetscErrorCode DMPlexCheck(DM dm)
9348 {
9349   PetscInt cellHeight;
9350 
9351   PetscFunctionBegin;
9352   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9353   PetscCall(DMPlexCheckSymmetry(dm));
9354   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9355   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9356   PetscCall(DMPlexCheckGeometry(dm));
9357   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9358   PetscCall(DMPlexCheckInterfaceCones(dm));
9359   PetscFunctionReturn(PETSC_SUCCESS);
9360 }
9361 
9362 typedef struct cell_stats {
9363   PetscReal min, max, sum, squaresum;
9364   PetscInt  count;
9365 } cell_stats_t;
9366 
9367 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9368 {
9369   PetscInt i, N = *len;
9370 
9371   for (i = 0; i < N; i++) {
9372     cell_stats_t *A = (cell_stats_t *)a;
9373     cell_stats_t *B = (cell_stats_t *)b;
9374 
9375     B->min = PetscMin(A->min, B->min);
9376     B->max = PetscMax(A->max, B->max);
9377     B->sum += A->sum;
9378     B->squaresum += A->squaresum;
9379     B->count += A->count;
9380   }
9381 }
9382 
9383 /*@
9384   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9385 
9386   Collective
9387 
9388   Input Parameters:
9389 + dm        - The `DMPLEX` object
9390 . output    - If true, statistics will be displayed on `stdout`
9391 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9392 
9393   Level: developer
9394 
9395   Notes:
9396   This is mainly intended for debugging/testing purposes.
9397 
9398   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9399 
9400 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9401 @*/
9402 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9403 {
9404   DM           dmCoarse;
9405   cell_stats_t stats, globalStats;
9406   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9407   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9408   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9409   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9410   PetscMPIInt  rank, size;
9411 
9412   PetscFunctionBegin;
9413   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9414   stats.min = PETSC_MAX_REAL;
9415   stats.max = PETSC_MIN_REAL;
9416   stats.sum = stats.squaresum = 0.;
9417   stats.count                 = 0;
9418 
9419   PetscCallMPI(MPI_Comm_size(comm, &size));
9420   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9421   PetscCall(DMGetCoordinateDim(dm, &cdim));
9422   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9423   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9424   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9425   for (c = cStart; c < cEnd; c++) {
9426     PetscInt  i;
9427     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9428 
9429     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9430     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9431     for (i = 0; i < PetscSqr(cdim); ++i) {
9432       frobJ += J[i] * J[i];
9433       frobInvJ += invJ[i] * invJ[i];
9434     }
9435     cond2 = frobJ * frobInvJ;
9436     cond  = PetscSqrtReal(cond2);
9437 
9438     stats.min = PetscMin(stats.min, cond);
9439     stats.max = PetscMax(stats.max, cond);
9440     stats.sum += cond;
9441     stats.squaresum += cond2;
9442     stats.count++;
9443     if (output && cond > limit) {
9444       PetscSection coordSection;
9445       Vec          coordsLocal;
9446       PetscScalar *coords = NULL;
9447       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9448 
9449       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9450       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9451       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9452       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9453       for (i = 0; i < Nv / cdim; ++i) {
9454         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9455         for (d = 0; d < cdim; ++d) {
9456           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9457           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9458         }
9459         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9460       }
9461       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9462       for (cl = 0; cl < clSize * 2; cl += 2) {
9463         const PetscInt edge = closure[cl];
9464 
9465         if ((edge >= eStart) && (edge < eEnd)) {
9466           PetscReal len;
9467 
9468           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9469           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9470         }
9471       }
9472       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9473       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9474     }
9475   }
9476   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9477 
9478   if (size > 1) {
9479     PetscMPIInt  blockLengths[2] = {4, 1};
9480     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9481     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9482     MPI_Op       statReduce;
9483 
9484     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9485     PetscCallMPI(MPI_Type_commit(&statType));
9486     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9487     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9488     PetscCallMPI(MPI_Op_free(&statReduce));
9489     PetscCallMPI(MPI_Type_free(&statType));
9490   } else {
9491     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9492   }
9493   if (rank == 0) {
9494     count = globalStats.count;
9495     min   = globalStats.min;
9496     max   = globalStats.max;
9497     mean  = globalStats.sum / globalStats.count;
9498     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9499   }
9500 
9501   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));
9502   PetscCall(PetscFree2(J, invJ));
9503 
9504   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9505   if (dmCoarse) {
9506     PetscBool isplex;
9507 
9508     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9509     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9510   }
9511   PetscFunctionReturn(PETSC_SUCCESS);
9512 }
9513 
9514 /*@
9515   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9516   orthogonal quality below given tolerance.
9517 
9518   Collective
9519 
9520   Input Parameters:
9521 + dm   - The `DMPLEX` object
9522 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9523 - atol - [0, 1] Absolute tolerance for tagging cells.
9524 
9525   Output Parameters:
9526 + OrthQual      - `Vec` containing orthogonal quality per cell
9527 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9528 
9529   Options Database Keys:
9530 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9531 - -dm_plex_orthogonal_quality_vec_view   - view OrthQual vector.
9532 
9533   Level: intermediate
9534 
9535   Notes:
9536   Orthogonal quality is given by the following formula\:
9537 
9538   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9539 
9540   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
9541   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9542   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9543   calculating the cosine of the angle between these vectors.
9544 
9545   Orthogonal quality ranges from 1 (best) to 0 (worst).
9546 
9547   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9548   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9549 
9550   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9551 
9552 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9553 @*/
9554 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9555 {
9556   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9557   PetscInt              *idx;
9558   PetscScalar           *oqVals;
9559   const PetscScalar     *cellGeomArr, *faceGeomArr;
9560   PetscReal             *ci, *fi, *Ai;
9561   MPI_Comm               comm;
9562   Vec                    cellgeom, facegeom;
9563   DM                     dmFace, dmCell;
9564   IS                     glob;
9565   ISLocalToGlobalMapping ltog;
9566   PetscViewer            vwr;
9567 
9568   PetscFunctionBegin;
9569   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9570   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9571   PetscAssertPointer(OrthQual, 4);
9572   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9573   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9574   PetscCall(DMGetDimension(dm, &nc));
9575   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9576   {
9577     DMPlexInterpolatedFlag interpFlag;
9578 
9579     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9580     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9581       PetscMPIInt rank;
9582 
9583       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9584       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9585     }
9586   }
9587   if (OrthQualLabel) {
9588     PetscAssertPointer(OrthQualLabel, 5);
9589     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9590     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9591   } else {
9592     *OrthQualLabel = NULL;
9593   }
9594   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9595   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9596   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
9597   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9598   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9599   PetscCall(VecCreate(comm, OrthQual));
9600   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9601   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9602   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9603   PetscCall(VecSetUp(*OrthQual));
9604   PetscCall(ISDestroy(&glob));
9605   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9606   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9607   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9608   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9609   PetscCall(VecGetDM(cellgeom, &dmCell));
9610   PetscCall(VecGetDM(facegeom, &dmFace));
9611   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9612   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
9613     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9614     PetscInt         cellarr[2], *adj = NULL;
9615     PetscScalar     *cArr, *fArr;
9616     PetscReal        minvalc = 1.0, minvalf = 1.0;
9617     PetscFVCellGeom *cg;
9618 
9619     idx[cellIter] = cell - cStart;
9620     cellarr[0]    = cell;
9621     /* Make indexing into cellGeom easier */
9622     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9623     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9624     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9625     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9626     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
9627       PetscInt         i;
9628       const PetscInt   neigh  = adj[cellneigh];
9629       PetscReal        normci = 0, normfi = 0, normai = 0;
9630       PetscFVCellGeom *cgneigh;
9631       PetscFVFaceGeom *fg;
9632 
9633       /* Don't count ourselves in the neighbor list */
9634       if (neigh == cell) continue;
9635       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9636       cellarr[1] = neigh;
9637       {
9638         PetscInt        numcovpts;
9639         const PetscInt *covpts;
9640 
9641         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9642         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9643         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9644       }
9645 
9646       /* Compute c_i, f_i and their norms */
9647       for (i = 0; i < nc; i++) {
9648         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9649         fi[i] = fg->centroid[i] - cg->centroid[i];
9650         Ai[i] = fg->normal[i];
9651         normci += PetscPowReal(ci[i], 2);
9652         normfi += PetscPowReal(fi[i], 2);
9653         normai += PetscPowReal(Ai[i], 2);
9654       }
9655       normci = PetscSqrtReal(normci);
9656       normfi = PetscSqrtReal(normfi);
9657       normai = PetscSqrtReal(normai);
9658 
9659       /* Normalize and compute for each face-cell-normal pair */
9660       for (i = 0; i < nc; i++) {
9661         ci[i] = ci[i] / normci;
9662         fi[i] = fi[i] / normfi;
9663         Ai[i] = Ai[i] / normai;
9664         /* PetscAbs because I don't know if normals are guaranteed to point out */
9665         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
9666         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
9667       }
9668       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
9669       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
9670     }
9671     PetscCall(PetscFree(adj));
9672     PetscCall(PetscFree2(cArr, fArr));
9673     /* Defer to cell if they're equal */
9674     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9675     if (OrthQualLabel) {
9676       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9677     }
9678   }
9679   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
9680   PetscCall(VecAssemblyBegin(*OrthQual));
9681   PetscCall(VecAssemblyEnd(*OrthQual));
9682   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9683   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9684   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9685   if (OrthQualLabel) {
9686     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9687   }
9688   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9689   PetscCall(PetscOptionsRestoreViewer(&vwr));
9690   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9691   PetscFunctionReturn(PETSC_SUCCESS);
9692 }
9693 
9694 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9695  * interpolator construction */
9696 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9697 {
9698   PetscSection section, newSection, gsection;
9699   PetscSF      sf;
9700   PetscBool    hasConstraints, ghasConstraints;
9701 
9702   PetscFunctionBegin;
9703   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9704   PetscAssertPointer(odm, 2);
9705   PetscCall(DMGetLocalSection(dm, &section));
9706   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9707   PetscCall(MPIU_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
9708   if (!ghasConstraints) {
9709     PetscCall(PetscObjectReference((PetscObject)dm));
9710     *odm = dm;
9711     PetscFunctionReturn(PETSC_SUCCESS);
9712   }
9713   PetscCall(DMClone(dm, odm));
9714   PetscCall(DMCopyFields(dm, *odm));
9715   PetscCall(DMGetLocalSection(*odm, &newSection));
9716   PetscCall(DMGetPointSF(*odm, &sf));
9717   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_FALSE, &gsection));
9718   PetscCall(DMSetGlobalSection(*odm, gsection));
9719   PetscCall(PetscSectionDestroy(&gsection));
9720   PetscFunctionReturn(PETSC_SUCCESS);
9721 }
9722 
9723 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9724 {
9725   DM        dmco, dmfo;
9726   Mat       interpo;
9727   Vec       rscale;
9728   Vec       cglobalo, clocal;
9729   Vec       fglobal, fglobalo, flocal;
9730   PetscBool regular;
9731 
9732   PetscFunctionBegin;
9733   PetscCall(DMGetFullDM(dmc, &dmco));
9734   PetscCall(DMGetFullDM(dmf, &dmfo));
9735   PetscCall(DMSetCoarseDM(dmfo, dmco));
9736   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9737   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9738   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9739   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9740   PetscCall(DMCreateLocalVector(dmc, &clocal));
9741   PetscCall(VecSet(cglobalo, 0.));
9742   PetscCall(VecSet(clocal, 0.));
9743   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9744   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9745   PetscCall(DMCreateLocalVector(dmf, &flocal));
9746   PetscCall(VecSet(fglobal, 0.));
9747   PetscCall(VecSet(fglobalo, 0.));
9748   PetscCall(VecSet(flocal, 0.));
9749   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9750   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9751   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9752   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9753   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9754   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9755   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9756   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9757   *shift = fglobal;
9758   PetscCall(VecDestroy(&flocal));
9759   PetscCall(VecDestroy(&fglobalo));
9760   PetscCall(VecDestroy(&clocal));
9761   PetscCall(VecDestroy(&cglobalo));
9762   PetscCall(VecDestroy(&rscale));
9763   PetscCall(MatDestroy(&interpo));
9764   PetscCall(DMDestroy(&dmfo));
9765   PetscCall(DMDestroy(&dmco));
9766   PetscFunctionReturn(PETSC_SUCCESS);
9767 }
9768 
9769 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
9770 {
9771   PetscObject shifto;
9772   Vec         shift;
9773 
9774   PetscFunctionBegin;
9775   if (!interp) {
9776     Vec rscale;
9777 
9778     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9779     PetscCall(VecDestroy(&rscale));
9780   } else {
9781     PetscCall(PetscObjectReference((PetscObject)interp));
9782   }
9783   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9784   if (!shifto) {
9785     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9786     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
9787     shifto = (PetscObject)shift;
9788     PetscCall(VecDestroy(&shift));
9789   }
9790   shift = (Vec)shifto;
9791   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9792   PetscCall(VecAXPY(fineSol, 1.0, shift));
9793   PetscCall(MatDestroy(&interp));
9794   PetscFunctionReturn(PETSC_SUCCESS);
9795 }
9796 
9797 /* Pointwise interpolation
9798      Just code FEM for now
9799      u^f = I u^c
9800      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9801      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9802      I_{ij} = psi^f_i phi^c_j
9803 */
9804 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
9805 {
9806   PetscSection gsc, gsf;
9807   PetscInt     m, n;
9808   void        *ctx;
9809   DM           cdm;
9810   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9811 
9812   PetscFunctionBegin;
9813   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9814   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9815   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9816   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9817 
9818   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9819   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
9820   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9821   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9822   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9823 
9824   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9825   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9826   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9827   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9828   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9829   if (scaling) {
9830     /* Use naive scaling */
9831     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
9832   }
9833   PetscFunctionReturn(PETSC_SUCCESS);
9834 }
9835 
9836 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
9837 {
9838   VecScatter ctx;
9839 
9840   PetscFunctionBegin;
9841   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
9842   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
9843   PetscCall(VecScatterDestroy(&ctx));
9844   PetscFunctionReturn(PETSC_SUCCESS);
9845 }
9846 
9847 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[])
9848 {
9849   const PetscInt Nc = uOff[1] - uOff[0];
9850   PetscInt       c;
9851   for (c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
9852 }
9853 
9854 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
9855 {
9856   DM           dmc;
9857   PetscDS      ds;
9858   Vec          ones, locmass;
9859   IS           cellIS;
9860   PetscFormKey key;
9861   PetscInt     depth;
9862 
9863   PetscFunctionBegin;
9864   PetscCall(DMClone(dm, &dmc));
9865   PetscCall(DMCopyDisc(dm, dmc));
9866   PetscCall(DMGetDS(dmc, &ds));
9867   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9868   PetscCall(DMCreateGlobalVector(dmc, mass));
9869   PetscCall(DMGetLocalVector(dmc, &ones));
9870   PetscCall(DMGetLocalVector(dmc, &locmass));
9871   PetscCall(DMPlexGetDepth(dmc, &depth));
9872   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9873   PetscCall(VecSet(locmass, 0.0));
9874   PetscCall(VecSet(ones, 1.0));
9875   key.label = NULL;
9876   key.value = 0;
9877   key.field = 0;
9878   key.part  = 0;
9879   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
9880   PetscCall(ISDestroy(&cellIS));
9881   PetscCall(VecSet(*mass, 0.0));
9882   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
9883   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
9884   PetscCall(DMRestoreLocalVector(dmc, &ones));
9885   PetscCall(DMRestoreLocalVector(dmc, &locmass));
9886   PetscCall(DMDestroy(&dmc));
9887   PetscFunctionReturn(PETSC_SUCCESS);
9888 }
9889 
9890 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
9891 {
9892   PetscSection gsc, gsf;
9893   PetscInt     m, n;
9894   void        *ctx;
9895   DM           cdm;
9896   PetscBool    regular;
9897 
9898   PetscFunctionBegin;
9899   if (dmFine == dmCoarse) {
9900     DM            dmc;
9901     PetscDS       ds;
9902     PetscWeakForm wf;
9903     Vec           u;
9904     IS            cellIS;
9905     PetscFormKey  key;
9906     PetscInt      depth;
9907 
9908     PetscCall(DMClone(dmFine, &dmc));
9909     PetscCall(DMCopyDisc(dmFine, dmc));
9910     PetscCall(DMGetDS(dmc, &ds));
9911     PetscCall(PetscDSGetWeakForm(ds, &wf));
9912     PetscCall(PetscWeakFormClear(wf));
9913     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9914     PetscCall(DMCreateMatrix(dmc, mass));
9915     PetscCall(DMGetLocalVector(dmc, &u));
9916     PetscCall(DMPlexGetDepth(dmc, &depth));
9917     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9918     PetscCall(MatZeroEntries(*mass));
9919     key.label = NULL;
9920     key.value = 0;
9921     key.field = 0;
9922     key.part  = 0;
9923     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
9924     PetscCall(ISDestroy(&cellIS));
9925     PetscCall(DMRestoreLocalVector(dmc, &u));
9926     PetscCall(DMDestroy(&dmc));
9927   } else {
9928     PetscCall(DMGetGlobalSection(dmFine, &gsf));
9929     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9930     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9931     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9932 
9933     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
9934     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9935     PetscCall(MatSetType(*mass, dmCoarse->mattype));
9936     PetscCall(DMGetApplicationContext(dmFine, &ctx));
9937 
9938     PetscCall(DMGetCoarseDM(dmFine, &cdm));
9939     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9940     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
9941     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
9942   }
9943   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
9944   PetscFunctionReturn(PETSC_SUCCESS);
9945 }
9946 
9947 /*@
9948   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9949 
9950   Input Parameter:
9951 . dm - The `DMPLEX` object
9952 
9953   Output Parameter:
9954 . regular - The flag
9955 
9956   Level: intermediate
9957 
9958 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
9959 @*/
9960 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
9961 {
9962   PetscFunctionBegin;
9963   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9964   PetscAssertPointer(regular, 2);
9965   *regular = ((DM_Plex *)dm->data)->regularRefinement;
9966   PetscFunctionReturn(PETSC_SUCCESS);
9967 }
9968 
9969 /*@
9970   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9971 
9972   Input Parameters:
9973 + dm      - The `DMPLEX` object
9974 - regular - The flag
9975 
9976   Level: intermediate
9977 
9978 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
9979 @*/
9980 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
9981 {
9982   PetscFunctionBegin;
9983   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9984   ((DM_Plex *)dm->data)->regularRefinement = regular;
9985   PetscFunctionReturn(PETSC_SUCCESS);
9986 }
9987 
9988 /*@
9989   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
9990   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
9991 
9992   Not Collective
9993 
9994   Input Parameter:
9995 . dm - The `DMPLEX` object
9996 
9997   Output Parameters:
9998 + anchorSection - If not `NULL`, set to the section describing which points anchor the constrained points.
9999 - anchorIS      - If not `NULL`, set to the list of anchors indexed by `anchorSection`
10000 
10001   Level: intermediate
10002 
10003 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
10004 @*/
10005 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
10006 {
10007   DM_Plex *plex = (DM_Plex *)dm->data;
10008 
10009   PetscFunctionBegin;
10010   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10011   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
10012   if (anchorSection) *anchorSection = plex->anchorSection;
10013   if (anchorIS) *anchorIS = plex->anchorIS;
10014   PetscFunctionReturn(PETSC_SUCCESS);
10015 }
10016 
10017 /*@
10018   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.
10019 
10020   Collective
10021 
10022   Input Parameters:
10023 + dm            - The `DMPLEX` object
10024 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
10025                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10026 - anchorIS      - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10027 
10028   Level: intermediate
10029 
10030   Notes:
10031   Unlike boundary conditions, when a point's degrees of freedom in a section are constrained to
10032   an outside value, the anchor constraints set a point's degrees of freedom to be a linear
10033   combination of other points' degrees of freedom.
10034 
10035   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
10036   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
10037 
10038   The reference counts of `anchorSection` and `anchorIS` are incremented.
10039 
10040 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
10041 @*/
10042 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
10043 {
10044   DM_Plex    *plex = (DM_Plex *)dm->data;
10045   PetscMPIInt result;
10046 
10047   PetscFunctionBegin;
10048   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10049   if (anchorSection) {
10050     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
10051     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
10052     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
10053   }
10054   if (anchorIS) {
10055     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
10056     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
10057     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
10058   }
10059 
10060   PetscCall(PetscObjectReference((PetscObject)anchorSection));
10061   PetscCall(PetscSectionDestroy(&plex->anchorSection));
10062   plex->anchorSection = anchorSection;
10063 
10064   PetscCall(PetscObjectReference((PetscObject)anchorIS));
10065   PetscCall(ISDestroy(&plex->anchorIS));
10066   plex->anchorIS = anchorIS;
10067 
10068   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
10069     PetscInt        size, a, pStart, pEnd;
10070     const PetscInt *anchors;
10071 
10072     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10073     PetscCall(ISGetLocalSize(anchorIS, &size));
10074     PetscCall(ISGetIndices(anchorIS, &anchors));
10075     for (a = 0; a < size; a++) {
10076       PetscInt p;
10077 
10078       p = anchors[a];
10079       if (p >= pStart && p < pEnd) {
10080         PetscInt dof;
10081 
10082         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10083         if (dof) {
10084           PetscCall(ISRestoreIndices(anchorIS, &anchors));
10085           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
10086         }
10087       }
10088     }
10089     PetscCall(ISRestoreIndices(anchorIS, &anchors));
10090   }
10091   /* reset the generic constraints */
10092   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
10093   PetscFunctionReturn(PETSC_SUCCESS);
10094 }
10095 
10096 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
10097 {
10098   PetscSection anchorSection;
10099   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
10100 
10101   PetscFunctionBegin;
10102   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10103   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10104   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
10105   PetscCall(PetscSectionGetNumFields(section, &numFields));
10106   if (numFields) {
10107     PetscInt f;
10108     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
10109 
10110     for (f = 0; f < numFields; f++) {
10111       PetscInt numComp;
10112 
10113       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
10114       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
10115     }
10116   }
10117   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10118   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10119   pStart = PetscMax(pStart, sStart);
10120   pEnd   = PetscMin(pEnd, sEnd);
10121   pEnd   = PetscMax(pStart, pEnd);
10122   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
10123   for (p = pStart; p < pEnd; p++) {
10124     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10125     if (dof) {
10126       PetscCall(PetscSectionGetDof(section, p, &dof));
10127       PetscCall(PetscSectionSetDof(*cSec, p, dof));
10128       for (f = 0; f < numFields; f++) {
10129         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
10130         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
10131       }
10132     }
10133   }
10134   PetscCall(PetscSectionSetUp(*cSec));
10135   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
10136   PetscFunctionReturn(PETSC_SUCCESS);
10137 }
10138 
10139 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
10140 {
10141   PetscSection    aSec;
10142   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
10143   const PetscInt *anchors;
10144   PetscInt        numFields, f;
10145   IS              aIS;
10146   MatType         mtype;
10147   PetscBool       iscuda, iskokkos;
10148 
10149   PetscFunctionBegin;
10150   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10151   PetscCall(PetscSectionGetStorageSize(cSec, &m));
10152   PetscCall(PetscSectionGetStorageSize(section, &n));
10153   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
10154   PetscCall(MatSetSizes(*cMat, m, n, m, n));
10155   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
10156   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
10157   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
10158   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
10159   if (iscuda) mtype = MATSEQAIJCUSPARSE;
10160   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
10161   else mtype = MATSEQAIJ;
10162   PetscCall(MatSetType(*cMat, mtype));
10163   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
10164   PetscCall(ISGetIndices(aIS, &anchors));
10165   /* cSec will be a subset of aSec and section */
10166   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
10167   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10168   PetscCall(PetscMalloc1(m + 1, &i));
10169   i[0] = 0;
10170   PetscCall(PetscSectionGetNumFields(section, &numFields));
10171   for (p = pStart; p < pEnd; p++) {
10172     PetscInt rDof, rOff, r;
10173 
10174     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10175     if (!rDof) continue;
10176     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10177     if (numFields) {
10178       for (f = 0; f < numFields; f++) {
10179         annz = 0;
10180         for (r = 0; r < rDof; r++) {
10181           a = anchors[rOff + r];
10182           if (a < sStart || a >= sEnd) continue;
10183           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10184           annz += aDof;
10185         }
10186         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10187         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
10188         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10189       }
10190     } else {
10191       annz = 0;
10192       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10193       for (q = 0; q < dof; q++) {
10194         a = anchors[rOff + q];
10195         if (a < sStart || a >= sEnd) continue;
10196         PetscCall(PetscSectionGetDof(section, a, &aDof));
10197         annz += aDof;
10198       }
10199       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10200       PetscCall(PetscSectionGetOffset(cSec, p, &off));
10201       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10202     }
10203   }
10204   nnz = i[m];
10205   PetscCall(PetscMalloc1(nnz, &j));
10206   offset = 0;
10207   for (p = pStart; p < pEnd; p++) {
10208     if (numFields) {
10209       for (f = 0; f < numFields; f++) {
10210         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10211         for (q = 0; q < dof; q++) {
10212           PetscInt rDof, rOff, r;
10213           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10214           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10215           for (r = 0; r < rDof; r++) {
10216             PetscInt s;
10217 
10218             a = anchors[rOff + r];
10219             if (a < sStart || a >= sEnd) continue;
10220             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10221             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
10222             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10223           }
10224         }
10225       }
10226     } else {
10227       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10228       for (q = 0; q < dof; q++) {
10229         PetscInt rDof, rOff, r;
10230         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10231         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10232         for (r = 0; r < rDof; r++) {
10233           PetscInt s;
10234 
10235           a = anchors[rOff + r];
10236           if (a < sStart || a >= sEnd) continue;
10237           PetscCall(PetscSectionGetDof(section, a, &aDof));
10238           PetscCall(PetscSectionGetOffset(section, a, &aOff));
10239           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10240         }
10241       }
10242     }
10243   }
10244   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
10245   PetscCall(PetscFree(i));
10246   PetscCall(PetscFree(j));
10247   PetscCall(ISRestoreIndices(aIS, &anchors));
10248   PetscFunctionReturn(PETSC_SUCCESS);
10249 }
10250 
10251 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
10252 {
10253   DM_Plex     *plex = (DM_Plex *)dm->data;
10254   PetscSection anchorSection, section, cSec;
10255   Mat          cMat;
10256 
10257   PetscFunctionBegin;
10258   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10259   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10260   if (anchorSection) {
10261     PetscInt Nf;
10262 
10263     PetscCall(DMGetLocalSection(dm, &section));
10264     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
10265     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
10266     PetscCall(DMGetNumFields(dm, &Nf));
10267     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
10268     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
10269     PetscCall(PetscSectionDestroy(&cSec));
10270     PetscCall(MatDestroy(&cMat));
10271   }
10272   PetscFunctionReturn(PETSC_SUCCESS);
10273 }
10274 
10275 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10276 {
10277   IS           subis;
10278   PetscSection section, subsection;
10279 
10280   PetscFunctionBegin;
10281   PetscCall(DMGetLocalSection(dm, &section));
10282   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10283   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10284   /* Create subdomain */
10285   PetscCall(DMPlexFilter(dm, label, value, subdm));
10286   /* Create submodel */
10287   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10288   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10289   PetscCall(DMSetLocalSection(*subdm, subsection));
10290   PetscCall(PetscSectionDestroy(&subsection));
10291   PetscCall(DMCopyDisc(dm, *subdm));
10292   /* Create map from submodel to global model */
10293   if (is) {
10294     PetscSection    sectionGlobal, subsectionGlobal;
10295     IS              spIS;
10296     const PetscInt *spmap;
10297     PetscInt       *subIndices;
10298     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10299     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10300 
10301     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10302     PetscCall(ISGetIndices(spIS, &spmap));
10303     PetscCall(PetscSectionGetNumFields(section, &Nf));
10304     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10305     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10306     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10307     for (p = pStart; p < pEnd; ++p) {
10308       PetscInt gdof, pSubSize = 0;
10309 
10310       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10311       if (gdof > 0) {
10312         for (f = 0; f < Nf; ++f) {
10313           PetscInt fdof, fcdof;
10314 
10315           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10316           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10317           pSubSize += fdof - fcdof;
10318         }
10319         subSize += pSubSize;
10320         if (pSubSize) {
10321           if (bs < 0) {
10322             bs = pSubSize;
10323           } else if (bs != pSubSize) {
10324             /* Layout does not admit a pointwise block size */
10325             bs = 1;
10326           }
10327         }
10328       }
10329     }
10330     /* Must have same blocksize on all procs (some might have no points) */
10331     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
10332     bsLocal[1] = bs;
10333     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10334     if (bsMinMax[0] != bsMinMax[1]) {
10335       bs = 1;
10336     } else {
10337       bs = bsMinMax[0];
10338     }
10339     PetscCall(PetscMalloc1(subSize, &subIndices));
10340     for (p = pStart; p < pEnd; ++p) {
10341       PetscInt gdof, goff;
10342 
10343       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10344       if (gdof > 0) {
10345         const PetscInt point = spmap[p];
10346 
10347         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10348         for (f = 0; f < Nf; ++f) {
10349           PetscInt fdof, fcdof, fc, f2, poff = 0;
10350 
10351           /* Can get rid of this loop by storing field information in the global section */
10352           for (f2 = 0; f2 < f; ++f2) {
10353             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10354             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10355             poff += fdof - fcdof;
10356           }
10357           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10358           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10359           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10360         }
10361       }
10362     }
10363     PetscCall(ISRestoreIndices(spIS, &spmap));
10364     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10365     if (bs > 1) {
10366       /* We need to check that the block size does not come from non-contiguous fields */
10367       PetscInt i, j, set = 1;
10368       for (i = 0; i < subSize; i += bs) {
10369         for (j = 0; j < bs; ++j) {
10370           if (subIndices[i + j] != subIndices[i] + j) {
10371             set = 0;
10372             break;
10373           }
10374         }
10375       }
10376       if (set) PetscCall(ISSetBlockSize(*is, bs));
10377     }
10378     /* Attach nullspace */
10379     for (f = 0; f < Nf; ++f) {
10380       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10381       if ((*subdm)->nullspaceConstructors[f]) break;
10382     }
10383     if (f < Nf) {
10384       MatNullSpace nullSpace;
10385       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10386 
10387       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10388       PetscCall(MatNullSpaceDestroy(&nullSpace));
10389     }
10390   }
10391   PetscFunctionReturn(PETSC_SUCCESS);
10392 }
10393 
10394 /*@
10395   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10396 
10397   Input Parameters:
10398 + dm    - The `DM`
10399 - dummy - unused argument
10400 
10401   Options Database Key:
10402 . -dm_plex_monitor_throughput - Activate the monitor
10403 
10404   Level: developer
10405 
10406 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10407 @*/
10408 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10409 {
10410   PetscLogHandler default_handler;
10411 
10412   PetscFunctionBegin;
10413   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10414   PetscCall(PetscLogGetDefaultHandler(&default_handler));
10415   if (default_handler) {
10416     PetscLogEvent      event;
10417     PetscEventPerfInfo eventInfo;
10418     PetscReal          cellRate, flopRate;
10419     PetscInt           cStart, cEnd, Nf, N;
10420     const char        *name;
10421 
10422     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10423     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10424     PetscCall(DMGetNumFields(dm, &Nf));
10425     PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10426     PetscCall(PetscLogEventGetPerfInfo(PETSC_DEFAULT, event, &eventInfo));
10427     N        = (cEnd - cStart) * Nf * eventInfo.count;
10428     flopRate = eventInfo.flops / eventInfo.time;
10429     cellRate = N / eventInfo.time;
10430     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)));
10431   } else {
10432     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.");
10433   }
10434   PetscFunctionReturn(PETSC_SUCCESS);
10435 }
10436