xref: /petsc/src/dm/impls/plex/plex.c (revision d027ae8252c8433bd30a9744018d99f45e0735bd)
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 #include <petscblaslapack.h>
12 
13 /* Logging support */
14 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;
15 PetscLogEvent DMPLEX_RebalBuildGraph, DMPLEX_RebalRewriteSF, DMPLEX_RebalGatherGraph, DMPLEX_RebalPartition, DMPLEX_RebalScatterPart, DMPLEX_Generate, DMPLEX_Transform, DMPLEX_GetLocalOffsets, DMPLEX_Uninterpolate;
16 
17 PetscBool  Plexcite       = PETSC_FALSE;
18 const char PlexCitation[] = "@article{LangeMitchellKnepleyGorman2015,\n"
19                             "title     = {Efficient mesh management in {Firedrake} using {PETSc-DMPlex}},\n"
20                             "author    = {Michael Lange and Lawrence Mitchell and Matthew G. Knepley and Gerard J. Gorman},\n"
21                             "journal   = {SIAM Journal on Scientific Computing},\n"
22                             "volume    = {38},\n"
23                             "number    = {5},\n"
24                             "pages     = {S143--S155},\n"
25                             "eprint    = {http://arxiv.org/abs/1506.07749},\n"
26                             "doi       = {10.1137/15M1026092},\n"
27                             "year      = {2016},\n"
28                             "petsc_uses={DMPlex},\n}\n";
29 
30 PETSC_EXTERN PetscErrorCode VecView_MPI(Vec, PetscViewer);
31 
32 /*@
33   DMPlexIsSimplex - Is the first cell in this mesh a simplex?
34 
35   Input Parameter:
36 . dm - The `DMPLEX` object
37 
38   Output Parameter:
39 . simplex - Flag checking for a simplex
40 
41   Level: intermediate
42 
43   Note:
44   This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
45   If the mesh has no cells, this returns `PETSC_FALSE`.
46 
47 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSimplexOrBoxCells()`, `DMPlexGetCellType()`, `DMPlexGetHeightStratum()`, `DMPolytopeTypeGetNumVertices()`
48 @*/
49 PetscErrorCode DMPlexIsSimplex(DM dm, PetscBool *simplex)
50 {
51   DMPolytopeType ct;
52   PetscInt       cStart, cEnd;
53 
54   PetscFunctionBegin;
55   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
56   if (cEnd <= cStart) {
57     *simplex = PETSC_FALSE;
58     PetscFunctionReturn(PETSC_SUCCESS);
59   }
60   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
61   *simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
62   PetscFunctionReturn(PETSC_SUCCESS);
63 }
64 
65 /*@
66   DMPlexGetSimplexOrBoxCells - Get the range of cells which are neither prisms nor ghost FV cells
67 
68   Input Parameters:
69 + dm     - The `DMPLEX` object
70 - height - The cell height in the Plex, 0 is the default
71 
72   Output Parameters:
73 + cStart - The first "normal" cell
74 - cEnd   - The upper bound on "normal" cells
75 
76   Level: developer
77 
78   Note:
79   This function requires that tensor cells are ordered last.
80 
81 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetCellTypeStratum()`
82 @*/
83 PetscErrorCode DMPlexGetSimplexOrBoxCells(DM dm, PetscInt height, PetscInt *cStart, PetscInt *cEnd)
84 {
85   DMLabel         ctLabel;
86   IS              valueIS;
87   const PetscInt *ctypes;
88   PetscBool       found = PETSC_FALSE;
89   PetscInt        Nct, cS = PETSC_MAX_INT, cE = 0;
90 
91   PetscFunctionBegin;
92   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
93   PetscCall(DMLabelGetValueIS(ctLabel, &valueIS));
94   PetscCall(ISGetLocalSize(valueIS, &Nct));
95   PetscCall(ISGetIndices(valueIS, &ctypes));
96   for (PetscInt t = 0; t < Nct; ++t) {
97     const DMPolytopeType ct = (DMPolytopeType)ctypes[t];
98     PetscInt             ctS, ctE, ht;
99 
100     if (ct == DM_POLYTOPE_UNKNOWN) {
101       // If any cells are not typed, just use all cells
102       PetscCall(DMPlexGetHeightStratum(dm, PetscMax(height, 0), cStart, cEnd));
103       break;
104     }
105     if (DMPolytopeTypeIsHybrid(ct) || ct == DM_POLYTOPE_FV_GHOST) continue;
106     PetscCall(DMLabelGetStratumBounds(ctLabel, ct, &ctS, &ctE));
107     if (ctS >= ctE) continue;
108     // Check that a point has the right height
109     PetscCall(DMPlexGetPointHeight(dm, ctS, &ht));
110     if (ht != height) continue;
111     cS    = PetscMin(cS, ctS);
112     cE    = PetscMax(cE, ctE);
113     found = PETSC_TRUE;
114   }
115   if (!Nct || !found) cS = cE = 0;
116   PetscCall(ISDestroy(&valueIS));
117   // Reset label for fast lookup
118   PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
119   if (cStart) *cStart = cS;
120   if (cEnd) *cEnd = cE;
121   PetscFunctionReturn(PETSC_SUCCESS);
122 }
123 
124 PetscErrorCode DMPlexGetFieldTypes_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *types, PetscInt **ssStart, PetscInt **ssEnd, PetscViewerVTKFieldType **sft)
125 {
126   PetscInt                 cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd, c, depth, cellHeight, t;
127   PetscInt                *sStart, *sEnd;
128   PetscViewerVTKFieldType *ft;
129   PetscInt                 vcdof[DM_NUM_POLYTOPES + 1], globalvcdof[DM_NUM_POLYTOPES + 1];
130   DMLabel                  depthLabel, ctLabel;
131 
132   PetscFunctionBegin;
133   /* the vcdof and globalvcdof are sized to allow every polytope type and simple vertex at DM_NUM_POLYTOPES */
134   PetscCall(PetscArrayzero(vcdof, DM_NUM_POLYTOPES + 1));
135   PetscCall(DMGetCoordinateDim(dm, &cdim));
136   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
137   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
138   if (field >= 0) {
139     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[DM_NUM_POLYTOPES]));
140   } else {
141     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[DM_NUM_POLYTOPES]));
142   }
143 
144   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
145   PetscCall(DMPlexGetDepth(dm, &depth));
146   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
147   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
148   for (c = 0; c < DM_NUM_POLYTOPES; ++c) {
149     const DMPolytopeType ict = (DMPolytopeType)c;
150     PetscInt             dep;
151 
152     if (ict == DM_POLYTOPE_FV_GHOST) continue;
153     PetscCall(DMLabelGetStratumBounds(ctLabel, ict, &cStart, &cEnd));
154     if (pStart >= 0) {
155       PetscCall(DMLabelGetValue(depthLabel, cStart, &dep));
156       if (dep != depth - cellHeight) continue;
157     }
158     if (field >= 0) {
159       if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[c]));
160     } else {
161       if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[c]));
162     }
163   }
164 
165   PetscCall(MPIU_Allreduce(vcdof, globalvcdof, DM_NUM_POLYTOPES + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
166   *types = 0;
167 
168   for (c = 0; c < DM_NUM_POLYTOPES + 1; ++c) {
169     if (globalvcdof[c]) ++(*types);
170   }
171 
172   PetscCall(PetscMalloc3(*types, &sStart, *types, &sEnd, *types, &ft));
173   t = 0;
174   if (globalvcdof[DM_NUM_POLYTOPES]) {
175     sStart[t] = vStart;
176     sEnd[t]   = vEnd;
177     ft[t]     = (globalvcdof[t] == cdim) ? PETSC_VTK_POINT_VECTOR_FIELD : PETSC_VTK_POINT_FIELD;
178     ++t;
179   }
180 
181   for (c = 0; c < DM_NUM_POLYTOPES; ++c) {
182     if (globalvcdof[c]) {
183       const DMPolytopeType ict = (DMPolytopeType)c;
184 
185       PetscCall(DMLabelGetStratumBounds(ctLabel, ict, &cStart, &cEnd));
186       sStart[t] = cStart;
187       sEnd[t]   = cEnd;
188       ft[t]     = (globalvcdof[c] == cdim) ? PETSC_VTK_CELL_VECTOR_FIELD : PETSC_VTK_CELL_FIELD;
189       ++t;
190     }
191   }
192 
193   if (!*types) {
194     if (field >= 0) {
195       const char *fieldname;
196 
197       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
198       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
199     } else {
200       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
201     }
202   }
203 
204   *ssStart = sStart;
205   *ssEnd   = sEnd;
206   *sft     = ft;
207   PetscFunctionReturn(PETSC_SUCCESS);
208 }
209 
210 PetscErrorCode DMPlexRestoreFieldTypes_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *types, PetscInt **sStart, PetscInt **sEnd, PetscViewerVTKFieldType **ft)
211 {
212   PetscFunctionBegin;
213   PetscCall(PetscFree3(*sStart, *sEnd, *ft));
214   PetscFunctionReturn(PETSC_SUCCESS);
215 }
216 
217 PetscErrorCode DMPlexGetFieldType_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *sStart, PetscInt *sEnd, PetscViewerVTKFieldType *ft)
218 {
219   PetscInt cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd;
220   PetscInt vcdof[2] = {0, 0}, globalvcdof[2];
221 
222   PetscFunctionBegin;
223   *ft = PETSC_VTK_INVALID;
224   PetscCall(DMGetCoordinateDim(dm, &cdim));
225   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
226   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
227   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
228   if (field >= 0) {
229     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[0]));
230     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[1]));
231   } else {
232     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[0]));
233     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[1]));
234   }
235   PetscCall(MPIU_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
236   if (globalvcdof[0]) {
237     *sStart = vStart;
238     *sEnd   = vEnd;
239     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
240     else *ft = PETSC_VTK_POINT_FIELD;
241   } else if (globalvcdof[1]) {
242     *sStart = cStart;
243     *sEnd   = cEnd;
244     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
245     else *ft = PETSC_VTK_CELL_FIELD;
246   } else {
247     if (field >= 0) {
248       const char *fieldname;
249 
250       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
251       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
252     } else {
253       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
254     }
255   }
256   PetscFunctionReturn(PETSC_SUCCESS);
257 }
258 
259 /*@
260   DMPlexVecView1D - Plot many 1D solutions on the same line graph
261 
262   Collective
263 
264   Input Parameters:
265 + dm     - The `DMPLEX` object
266 . n      - The number of vectors
267 . u      - The array of local vectors
268 - viewer - The `PetscViewer`
269 
270   Level: advanced
271 
272 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `VecViewFromOptions()`, `VecView()`
273 @*/
274 PetscErrorCode DMPlexVecView1D(DM dm, PetscInt n, Vec u[], PetscViewer viewer)
275 {
276   PetscDS            ds;
277   PetscDraw          draw = NULL;
278   PetscDrawLG        lg;
279   Vec                coordinates;
280   const PetscScalar *coords, **sol;
281   PetscReal         *vals;
282   PetscInt          *Nc;
283   PetscInt           Nf, f, c, Nl, l, i, vStart, vEnd, v;
284   char             **names;
285 
286   PetscFunctionBegin;
287   PetscCall(DMGetDS(dm, &ds));
288   PetscCall(PetscDSGetNumFields(ds, &Nf));
289   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
290   PetscCall(PetscDSGetComponents(ds, &Nc));
291 
292   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
293   if (!draw) PetscFunctionReturn(PETSC_SUCCESS);
294   PetscCall(PetscDrawLGCreate(draw, n * Nl, &lg));
295 
296   PetscCall(PetscMalloc3(n, &sol, n * Nl, &names, n * Nl, &vals));
297   for (i = 0, l = 0; i < n; ++i) {
298     const char *vname;
299 
300     PetscCall(PetscObjectGetName((PetscObject)u[i], &vname));
301     for (f = 0; f < Nf; ++f) {
302       PetscObject disc;
303       const char *fname;
304       char        tmpname[PETSC_MAX_PATH_LEN];
305 
306       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
307       /* TODO Create names for components */
308       for (c = 0; c < Nc[f]; ++c, ++l) {
309         PetscCall(PetscObjectGetName(disc, &fname));
310         PetscCall(PetscStrncpy(tmpname, vname, sizeof(tmpname)));
311         PetscCall(PetscStrlcat(tmpname, ":", sizeof(tmpname)));
312         PetscCall(PetscStrlcat(tmpname, fname, sizeof(tmpname)));
313         PetscCall(PetscStrallocpy(tmpname, &names[l]));
314       }
315     }
316   }
317   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *)names));
318   /* Just add P_1 support for now */
319   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
320   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
321   PetscCall(VecGetArrayRead(coordinates, &coords));
322   for (i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
323   for (v = vStart; v < vEnd; ++v) {
324     PetscScalar *x, *svals;
325 
326     PetscCall(DMPlexPointLocalRead(dm, v, coords, &x));
327     for (i = 0; i < n; ++i) {
328       PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
329       for (l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
330     }
331     PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
332   }
333   PetscCall(VecRestoreArrayRead(coordinates, &coords));
334   for (i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
335   for (l = 0; l < n * Nl; ++l) PetscCall(PetscFree(names[l]));
336   PetscCall(PetscFree3(sol, names, vals));
337 
338   PetscCall(PetscDrawLGDraw(lg));
339   PetscCall(PetscDrawLGDestroy(&lg));
340   PetscFunctionReturn(PETSC_SUCCESS);
341 }
342 
343 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer)
344 {
345   DM dm;
346 
347   PetscFunctionBegin;
348   PetscCall(VecGetDM(u, &dm));
349   PetscCall(DMPlexVecView1D(dm, 1, &u, viewer));
350   PetscFunctionReturn(PETSC_SUCCESS);
351 }
352 
353 static PetscErrorCode VecView_Plex_Local_Draw_2D(Vec v, PetscViewer viewer)
354 {
355   DM                 dm;
356   PetscSection       s;
357   PetscDraw          draw, popup;
358   DM                 cdm;
359   PetscSection       coordSection;
360   Vec                coordinates;
361   const PetscScalar *array;
362   PetscReal          lbound[3], ubound[3];
363   PetscReal          vbound[2], time;
364   PetscBool          flg;
365   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
366   const char        *name;
367   char               title[PETSC_MAX_PATH_LEN];
368 
369   PetscFunctionBegin;
370   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
371   PetscCall(VecGetDM(v, &dm));
372   PetscCall(DMGetCoordinateDim(dm, &dim));
373   PetscCall(DMGetLocalSection(dm, &s));
374   PetscCall(PetscSectionGetNumFields(s, &Nf));
375   PetscCall(DMGetCoarsenLevel(dm, &level));
376   PetscCall(DMGetCoordinateDM(dm, &cdm));
377   PetscCall(DMGetLocalSection(cdm, &coordSection));
378   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
379   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
380   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
381 
382   PetscCall(PetscObjectGetName((PetscObject)v, &name));
383   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
384 
385   PetscCall(VecGetLocalSize(coordinates, &N));
386   PetscCall(DMGetBoundingBox(dm, lbound, ubound));
387   PetscCall(PetscDrawClear(draw));
388 
389   /* Could implement something like DMDASelectFields() */
390   for (f = 0; f < Nf; ++f) {
391     DM          fdm = dm;
392     Vec         fv  = v;
393     IS          fis;
394     char        prefix[PETSC_MAX_PATH_LEN];
395     const char *fname;
396 
397     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
398     PetscCall(PetscSectionGetFieldName(s, f, &fname));
399 
400     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix, sizeof(prefix)));
401     else prefix[0] = '\0';
402     if (Nf > 1) {
403       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
404       PetscCall(VecGetSubVector(v, fis, &fv));
405       PetscCall(PetscStrlcat(prefix, fname, sizeof(prefix)));
406       PetscCall(PetscStrlcat(prefix, "_", sizeof(prefix)));
407     }
408     for (comp = 0; comp < Nc; ++comp, ++w) {
409       PetscInt nmax = 2;
410 
411       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
412       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
413       else PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
414       PetscCall(PetscDrawSetTitle(draw, title));
415 
416       /* TODO Get max and min only for this component */
417       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
418       if (!flg) {
419         PetscCall(VecMin(fv, NULL, &vbound[0]));
420         PetscCall(VecMax(fv, NULL, &vbound[1]));
421         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
422       }
423 
424       PetscCall(PetscDrawGetPopup(draw, &popup));
425       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
426       PetscCall(PetscDrawSetCoordinates(draw, lbound[0], lbound[1], ubound[0], ubound[1]));
427       PetscCall(VecGetArrayRead(fv, &array));
428       for (c = cStart; c < cEnd; ++c) {
429         DMPolytopeType     ct;
430         PetscScalar       *coords = NULL, *a = NULL;
431         const PetscScalar *coords_arr;
432         PetscBool          isDG;
433         PetscInt           numCoords, color[4] = {-1, -1, -1, -1};
434 
435         PetscCall(DMPlexGetCellType(dm, c, &ct));
436         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
437         if (a) {
438           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
439           color[1] = color[2] = color[3] = color[0];
440         } else {
441           PetscScalar *vals = NULL;
442           PetscInt     numVals, va;
443 
444           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
445           if (!numVals) {
446             PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
447             continue;
448           }
449           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);
450           switch (numVals / Nc) {
451           case 1: /* P1 Clamped Segment Prism */
452           case 2: /* P1 Segment Prism, P2 Clamped Segment Prism */
453             PetscCheck(ct == DM_POLYTOPE_SEG_PRISM_TENSOR, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell should be a tensor segment, but it is a %s", DMPolytopeTypes[ct]);
454             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
455             break;
456           case 3: /* P1 Triangle */
457           case 4: /* P1 Quadrangle */
458             PetscCheck(ct == DM_POLYTOPE_TRIANGLE || ct == DM_POLYTOPE_QUADRILATERAL || ct == DM_POLYTOPE_SEG_PRISM_TENSOR, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell should be a triangle or quad, but it is a %s", DMPolytopeTypes[ct]);
459             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
460             break;
461           case 6: /* P2 Triangle */
462           case 8: /* P2 Quadrangle */
463             PetscCheck(ct == DM_POLYTOPE_TRIANGLE || ct == DM_POLYTOPE_QUADRILATERAL || ct == DM_POLYTOPE_SEG_PRISM_TENSOR, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell should be a triangle or quad, but it is a %s", DMPolytopeTypes[ct]);
464             for (va = 0; va < numVals / (Nc * 2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp + numVals / (Nc * 2)]), vbound[0], vbound[1]);
465             break;
466           default:
467             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals / Nc);
468           }
469           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
470         }
471         PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
472         switch (numCoords) {
473         case 6:
474         case 12: /* Localized triangle */
475           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]));
476           break;
477         case 8:
478         case 16: /* Localized quadrilateral */
479           if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR) {
480             PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscMax(color[0], color[1])));
481           } else {
482             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]));
483             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]));
484           }
485           break;
486         default:
487           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
488         }
489         PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
490       }
491       PetscCall(VecRestoreArrayRead(fv, &array));
492       PetscCall(PetscDrawFlush(draw));
493       PetscCall(PetscDrawPause(draw));
494       PetscCall(PetscDrawSave(draw));
495     }
496     if (Nf > 1) {
497       PetscCall(VecRestoreSubVector(v, fis, &fv));
498       PetscCall(ISDestroy(&fis));
499       PetscCall(DMDestroy(&fdm));
500     }
501   }
502   PetscFunctionReturn(PETSC_SUCCESS);
503 }
504 
505 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
506 {
507   DM        dm;
508   PetscDraw draw;
509   PetscInt  dim;
510   PetscBool isnull;
511 
512   PetscFunctionBegin;
513   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
514   PetscCall(PetscDrawIsNull(draw, &isnull));
515   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
516 
517   PetscCall(VecGetDM(v, &dm));
518   PetscCall(DMGetCoordinateDim(dm, &dim));
519   switch (dim) {
520   case 1:
521     PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));
522     break;
523   case 2:
524     PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));
525     break;
526   default:
527     SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
528   }
529   PetscFunctionReturn(PETSC_SUCCESS);
530 }
531 
532 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
533 {
534   DM                      dm;
535   Vec                     locv;
536   const char             *name;
537   PetscSection            section;
538   PetscInt                pStart, pEnd;
539   PetscInt                numFields;
540   PetscViewerVTKFieldType ft;
541 
542   PetscFunctionBegin;
543   PetscCall(VecGetDM(v, &dm));
544   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
545   PetscCall(PetscObjectGetName((PetscObject)v, &name));
546   PetscCall(PetscObjectSetName((PetscObject)locv, name));
547   PetscCall(VecCopy(v, locv));
548   PetscCall(DMGetLocalSection(dm, &section));
549   PetscCall(PetscSectionGetNumFields(section, &numFields));
550   if (!numFields) {
551     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
552     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE, (PetscObject)locv));
553   } else {
554     PetscInt f;
555 
556     for (f = 0; f < numFields; f++) {
557       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
558       if (ft == PETSC_VTK_INVALID) continue;
559       PetscCall(PetscObjectReference((PetscObject)locv));
560       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE, (PetscObject)locv));
561     }
562     PetscCall(VecDestroy(&locv));
563   }
564   PetscFunctionReturn(PETSC_SUCCESS);
565 }
566 
567 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
568 {
569   DM        dm;
570   PetscBool isvtk, ishdf5, isdraw, isglvis, iscgns;
571 
572   PetscFunctionBegin;
573   PetscCall(VecGetDM(v, &dm));
574   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
575   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
576   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
577   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
578   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
579   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
580   if (isvtk || ishdf5 || isdraw || isglvis || iscgns) {
581     PetscInt    i, numFields;
582     PetscObject fe;
583     PetscBool   fem  = PETSC_FALSE;
584     Vec         locv = v;
585     const char *name;
586     PetscInt    step;
587     PetscReal   time;
588 
589     PetscCall(DMGetNumFields(dm, &numFields));
590     for (i = 0; i < numFields; i++) {
591       PetscCall(DMGetField(dm, i, NULL, &fe));
592       if (fe->classid == PETSCFE_CLASSID) {
593         fem = PETSC_TRUE;
594         break;
595       }
596     }
597     if (fem) {
598       PetscObject isZero;
599 
600       PetscCall(DMGetLocalVector(dm, &locv));
601       PetscCall(PetscObjectGetName((PetscObject)v, &name));
602       PetscCall(PetscObjectSetName((PetscObject)locv, name));
603       PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
604       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
605       PetscCall(VecCopy(v, locv));
606       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
607       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
608     }
609     if (isvtk) {
610       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
611     } else if (ishdf5) {
612 #if defined(PETSC_HAVE_HDF5)
613       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
614 #else
615       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
616 #endif
617     } else if (isdraw) {
618       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
619     } else if (isglvis) {
620       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
621       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
622       PetscCall(VecView_GLVis(locv, viewer));
623     } else if (iscgns) {
624 #if defined(PETSC_HAVE_CGNS)
625       PetscCall(VecView_Plex_Local_CGNS(locv, viewer));
626 #else
627       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
628 #endif
629     }
630     if (fem) {
631       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
632       PetscCall(DMRestoreLocalVector(dm, &locv));
633     }
634   } else {
635     PetscBool isseq;
636 
637     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
638     if (isseq) PetscCall(VecView_Seq(v, viewer));
639     else PetscCall(VecView_MPI(v, viewer));
640   }
641   PetscFunctionReturn(PETSC_SUCCESS);
642 }
643 
644 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
645 {
646   DM        dm;
647   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii, iscgns;
648 
649   PetscFunctionBegin;
650   PetscCall(VecGetDM(v, &dm));
651   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
652   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
653   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
654   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
655   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
656   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
657   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
658   if (isvtk || isdraw || isglvis || iscgns) {
659     Vec         locv;
660     PetscObject isZero;
661     const char *name;
662 
663     PetscCall(DMGetLocalVector(dm, &locv));
664     PetscCall(PetscObjectGetName((PetscObject)v, &name));
665     PetscCall(PetscObjectSetName((PetscObject)locv, name));
666     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
667     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
668     PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
669     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
670     PetscCall(VecView_Plex_Local(locv, viewer));
671     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
672     PetscCall(DMRestoreLocalVector(dm, &locv));
673     /* Call flush for proper logging of VecView timings */
674     if (isvtk) PetscCall(PetscViewerFlush(viewer));
675   } else if (ishdf5) {
676 #if defined(PETSC_HAVE_HDF5)
677     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
678 #else
679     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
680 #endif
681   } else if (isexodusii) {
682 #if defined(PETSC_HAVE_EXODUSII)
683     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
684 #else
685     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
686 #endif
687   } else {
688     PetscBool isseq;
689 
690     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
691     if (isseq) PetscCall(VecView_Seq(v, viewer));
692     else PetscCall(VecView_MPI(v, viewer));
693   }
694   PetscFunctionReturn(PETSC_SUCCESS);
695 }
696 
697 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
698 {
699   DM                dm;
700   MPI_Comm          comm;
701   PetscViewerFormat format;
702   Vec               v;
703   PetscBool         isvtk, ishdf5;
704 
705   PetscFunctionBegin;
706   PetscCall(VecGetDM(originalv, &dm));
707   PetscCall(PetscObjectGetComm((PetscObject)originalv, &comm));
708   PetscCheck(dm, comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
709   PetscCall(PetscViewerGetFormat(viewer, &format));
710   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
711   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
712   if (format == PETSC_VIEWER_NATIVE) {
713     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
714     /* this need a better fix */
715     if (dm->useNatural) {
716       if (dm->sfNatural) {
717         const char *vecname;
718         PetscInt    n, nroots;
719 
720         PetscCall(VecGetLocalSize(originalv, &n));
721         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
722         if (n == nroots) {
723           PetscCall(DMPlexCreateNaturalVector(dm, &v));
724           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
725           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
726           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
727           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
728         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
729       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
730     } else v = originalv;
731   } else v = originalv;
732 
733   if (ishdf5) {
734 #if defined(PETSC_HAVE_HDF5)
735     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
736 #else
737     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
738 #endif
739   } else if (isvtk) {
740     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
741   } else {
742     PetscBool isseq;
743 
744     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
745     if (isseq) PetscCall(VecView_Seq(v, viewer));
746     else PetscCall(VecView_MPI(v, viewer));
747   }
748   if (v != originalv) PetscCall(VecDestroy(&v));
749   PetscFunctionReturn(PETSC_SUCCESS);
750 }
751 
752 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
753 {
754   DM        dm;
755   PetscBool ishdf5;
756 
757   PetscFunctionBegin;
758   PetscCall(VecGetDM(v, &dm));
759   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
760   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
761   if (ishdf5) {
762     DM          dmBC;
763     Vec         gv;
764     const char *name;
765 
766     PetscCall(DMGetOutputDM(dm, &dmBC));
767     PetscCall(DMGetGlobalVector(dmBC, &gv));
768     PetscCall(PetscObjectGetName((PetscObject)v, &name));
769     PetscCall(PetscObjectSetName((PetscObject)gv, name));
770     PetscCall(VecLoad_Default(gv, viewer));
771     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
772     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
773     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
774   } else PetscCall(VecLoad_Default(v, viewer));
775   PetscFunctionReturn(PETSC_SUCCESS);
776 }
777 
778 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
779 {
780   DM        dm;
781   PetscBool ishdf5, isexodusii;
782 
783   PetscFunctionBegin;
784   PetscCall(VecGetDM(v, &dm));
785   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
786   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
787   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
788   if (ishdf5) {
789 #if defined(PETSC_HAVE_HDF5)
790     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
791 #else
792     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
793 #endif
794   } else if (isexodusii) {
795 #if defined(PETSC_HAVE_EXODUSII)
796     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
797 #else
798     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
799 #endif
800   } else PetscCall(VecLoad_Default(v, viewer));
801   PetscFunctionReturn(PETSC_SUCCESS);
802 }
803 
804 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
805 {
806   DM                dm;
807   PetscViewerFormat format;
808   PetscBool         ishdf5;
809 
810   PetscFunctionBegin;
811   PetscCall(VecGetDM(originalv, &dm));
812   PetscCheck(dm, PetscObjectComm((PetscObject)originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
813   PetscCall(PetscViewerGetFormat(viewer, &format));
814   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
815   if (format == PETSC_VIEWER_NATIVE) {
816     if (dm->useNatural) {
817       if (dm->sfNatural) {
818         if (ishdf5) {
819 #if defined(PETSC_HAVE_HDF5)
820           Vec         v;
821           const char *vecname;
822 
823           PetscCall(DMPlexCreateNaturalVector(dm, &v));
824           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
825           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
826           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
827           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
828           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
829           PetscCall(VecDestroy(&v));
830 #else
831           SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
832 #endif
833         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
834       }
835     } else PetscCall(VecLoad_Default(originalv, viewer));
836   }
837   PetscFunctionReturn(PETSC_SUCCESS);
838 }
839 
840 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
841 {
842   PetscSection       coordSection;
843   Vec                coordinates;
844   DMLabel            depthLabel, celltypeLabel;
845   const char        *name[4];
846   const PetscScalar *a;
847   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
848 
849   PetscFunctionBegin;
850   PetscCall(DMGetDimension(dm, &dim));
851   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
852   PetscCall(DMGetCoordinateSection(dm, &coordSection));
853   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
854   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
855   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
856   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
857   PetscCall(VecGetArrayRead(coordinates, &a));
858   name[0]       = "vertex";
859   name[1]       = "edge";
860   name[dim - 1] = "face";
861   name[dim]     = "cell";
862   for (c = cStart; c < cEnd; ++c) {
863     PetscInt *closure = NULL;
864     PetscInt  closureSize, cl, ct;
865 
866     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
867     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
868     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
869     PetscCall(PetscViewerASCIIPushTab(viewer));
870     for (cl = 0; cl < closureSize * 2; cl += 2) {
871       PetscInt point = closure[cl], depth, dof, off, d, p;
872 
873       if ((point < pStart) || (point >= pEnd)) continue;
874       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
875       if (!dof) continue;
876       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
877       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
878       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
879       for (p = 0; p < dof / dim; ++p) {
880         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
881         for (d = 0; d < dim; ++d) {
882           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
883           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double)PetscRealPart(a[off + p * dim + d])));
884         }
885         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
886       }
887       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
888     }
889     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
890     PetscCall(PetscViewerASCIIPopTab(viewer));
891   }
892   PetscCall(VecRestoreArrayRead(coordinates, &a));
893   PetscFunctionReturn(PETSC_SUCCESS);
894 }
895 
896 typedef enum {
897   CS_CARTESIAN,
898   CS_POLAR,
899   CS_CYLINDRICAL,
900   CS_SPHERICAL
901 } CoordSystem;
902 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
903 
904 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
905 {
906   PetscInt i;
907 
908   PetscFunctionBegin;
909   if (dim > 3) {
910     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)PetscRealPart(x[i])));
911   } else {
912     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
913 
914     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
915     switch (cs) {
916     case CS_CARTESIAN:
917       for (i = 0; i < dim; ++i) trcoords[i] = coords[i];
918       break;
919     case CS_POLAR:
920       PetscCheck(dim == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
921       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
922       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
923       break;
924     case CS_CYLINDRICAL:
925       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
926       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
927       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
928       trcoords[2] = coords[2];
929       break;
930     case CS_SPHERICAL:
931       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
932       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
933       trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
934       trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
935       break;
936     }
937     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)trcoords[i]));
938   }
939   PetscFunctionReturn(PETSC_SUCCESS);
940 }
941 
942 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
943 {
944   DM_Plex          *mesh = (DM_Plex *)dm->data;
945   DM                cdm, cdmCell;
946   PetscSection      coordSection, coordSectionCell;
947   Vec               coordinates, coordinatesCell;
948   PetscViewerFormat format;
949 
950   PetscFunctionBegin;
951   PetscCall(PetscViewerGetFormat(viewer, &format));
952   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
953     const char *name;
954     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
955     PetscInt    pStart, pEnd, p, numLabels, l;
956     PetscMPIInt rank, size;
957 
958     PetscCall(DMGetCoordinateDM(dm, &cdm));
959     PetscCall(DMGetCoordinateSection(dm, &coordSection));
960     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
961     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
962     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
963     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
964     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
965     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
966     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
967     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
968     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
969     PetscCall(DMGetDimension(dm, &dim));
970     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
971     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
972     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
973     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
974     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
975     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
976     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
977     for (p = pStart; p < pEnd; ++p) {
978       PetscInt dof, off, s;
979 
980       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
981       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
982       for (s = off; s < off + dof; ++s) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
983     }
984     PetscCall(PetscViewerFlush(viewer));
985     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
986     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
987     for (p = pStart; p < pEnd; ++p) {
988       PetscInt dof, off, c;
989 
990       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
991       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
992       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]));
993     }
994     PetscCall(PetscViewerFlush(viewer));
995     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
996     if (coordSection && coordinates) {
997       CoordSystem        cs = CS_CARTESIAN;
998       const PetscScalar *array, *arrayCell = NULL;
999       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_MAX_INT, pcEnd = PETSC_MIN_INT, pStart, pEnd, p;
1000       PetscMPIInt        rank;
1001       const char        *name;
1002 
1003       PetscCall(PetscOptionsGetEnum(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *)&cs, NULL));
1004       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
1005       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
1006       PetscCheck(Nf == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
1007       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
1008       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
1009       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
1010       pStart = PetscMin(pvStart, pcStart);
1011       pEnd   = PetscMax(pvEnd, pcEnd);
1012       PetscCall(PetscObjectGetName((PetscObject)coordinates, &name));
1013       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
1014       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
1015       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
1016 
1017       PetscCall(VecGetArrayRead(coordinates, &array));
1018       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
1019       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1020       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
1021       for (p = pStart; p < pEnd; ++p) {
1022         PetscInt dof, off;
1023 
1024         if (p >= pvStart && p < pvEnd) {
1025           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
1026           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
1027           if (dof) {
1028             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1029             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
1030             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1031           }
1032         }
1033         if (cdmCell && p >= pcStart && p < pcEnd) {
1034           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
1035           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
1036           if (dof) {
1037             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1038             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
1039             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1040           }
1041         }
1042       }
1043       PetscCall(PetscViewerFlush(viewer));
1044       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1045       PetscCall(VecRestoreArrayRead(coordinates, &array));
1046       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
1047     }
1048     PetscCall(DMGetNumLabels(dm, &numLabels));
1049     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1050     for (l = 0; l < numLabels; ++l) {
1051       DMLabel     label;
1052       PetscBool   isdepth;
1053       const char *name;
1054 
1055       PetscCall(DMGetLabelName(dm, l, &name));
1056       PetscCall(PetscStrcmp(name, "depth", &isdepth));
1057       if (isdepth) continue;
1058       PetscCall(DMGetLabel(dm, name, &label));
1059       PetscCall(DMLabelView(label, viewer));
1060     }
1061     if (size > 1) {
1062       PetscSF sf;
1063 
1064       PetscCall(DMGetPointSF(dm, &sf));
1065       PetscCall(PetscSFView(sf, viewer));
1066     }
1067     if (mesh->periodic.face_sfs)
1068       for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(PetscSFView(mesh->periodic.face_sfs[i], viewer));
1069     PetscCall(PetscViewerFlush(viewer));
1070   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
1071     const char  *name, *color;
1072     const char  *defcolors[3]  = {"gray", "orange", "green"};
1073     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
1074     char         lname[PETSC_MAX_PATH_LEN];
1075     PetscReal    scale      = 2.0;
1076     PetscReal    tikzscale  = 1.0;
1077     PetscBool    useNumbers = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
1078     double       tcoords[3];
1079     PetscScalar *coords;
1080     PetscInt     numLabels, l, numColors, numLColors, dim, d, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, fStart = 0, fEnd = 0, e, p, n;
1081     PetscMPIInt  rank, size;
1082     char       **names, **colors, **lcolors;
1083     PetscBool    flg, lflg;
1084     PetscBT      wp = NULL;
1085     PetscInt     pEnd, pStart;
1086 
1087     PetscCall(DMGetCoordinateDM(dm, &cdm));
1088     PetscCall(DMGetCoordinateSection(dm, &coordSection));
1089     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1090     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
1091     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
1092     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
1093     PetscCall(DMGetDimension(dm, &dim));
1094     PetscCall(DMPlexGetDepth(dm, &depth));
1095     PetscCall(DMGetNumLabels(dm, &numLabels));
1096     numLabels  = PetscMax(numLabels, 10);
1097     numColors  = 10;
1098     numLColors = 10;
1099     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
1100     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
1101     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
1102     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
1103     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
1104     for (d = 0; d < 4; ++d) drawColors[d] = PETSC_TRUE;
1105     n = 4;
1106     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
1107     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1108     n = 4;
1109     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
1110     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1111     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
1112     if (!useLabels) numLabels = 0;
1113     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
1114     if (!useColors) {
1115       numColors = 3;
1116       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
1117     }
1118     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
1119     if (!useColors) {
1120       numLColors = 4;
1121       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
1122     }
1123     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
1124     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
1125     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
1126     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
1127     if (depth < dim) plotEdges = PETSC_FALSE;
1128     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
1129 
1130     /* filter points with labelvalue != labeldefaultvalue */
1131     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1132     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1133     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
1134     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1135     PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
1136     if (lflg) {
1137       DMLabel lbl;
1138 
1139       PetscCall(DMGetLabel(dm, lname, &lbl));
1140       if (lbl) {
1141         PetscInt val, defval;
1142 
1143         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
1144         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
1145         for (c = pStart; c < pEnd; c++) {
1146           PetscInt *closure = NULL;
1147           PetscInt  closureSize;
1148 
1149           PetscCall(DMLabelGetValue(lbl, c, &val));
1150           if (val == defval) continue;
1151 
1152           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1153           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
1154           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1155         }
1156       }
1157     }
1158 
1159     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1160     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1161     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1162     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1163 \\documentclass[tikz]{standalone}\n\n\
1164 \\usepackage{pgflibraryshapes}\n\
1165 \\usetikzlibrary{backgrounds}\n\
1166 \\usetikzlibrary{arrows}\n\
1167 \\begin{document}\n"));
1168     if (size > 1) {
1169       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1170       for (p = 0; p < size; ++p) {
1171         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1172         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1173       }
1174       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1175     }
1176     if (drawHasse) {
1177       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, PetscMax(fEnd - fStart, cEnd - cStart)));
1178 
1179       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1180       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1181       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1182       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1183       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1184       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1185       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1186       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1187       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fStart}{%" PetscInt_FMT "}\n", fStart));
1188       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fEnd}{%" PetscInt_FMT "}\n", fEnd - 1));
1189       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fShift}{%.2f}\n", 3 + (maxStratum - (fEnd - fStart)) / 2.));
1190       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numFaces}{%" PetscInt_FMT "}\n", fEnd - fStart));
1191       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1192       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1193       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1194       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1195     }
1196     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1197 
1198     /* Plot vertices */
1199     PetscCall(VecGetArray(coordinates, &coords));
1200     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1201     for (v = vStart; v < vEnd; ++v) {
1202       PetscInt  off, dof, d;
1203       PetscBool isLabeled = PETSC_FALSE;
1204 
1205       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1206       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1207       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1208       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1209       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1210       for (d = 0; d < dof; ++d) {
1211         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1212         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1213       }
1214       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1215       if (dim == 3) {
1216         PetscReal tmp = tcoords[1];
1217         tcoords[1]    = tcoords[2];
1218         tcoords[2]    = -tmp;
1219       }
1220       for (d = 0; d < dof; ++d) {
1221         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1222         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1223       }
1224       if (drawHasse) color = colors[0 % numColors];
1225       else color = colors[rank % numColors];
1226       for (l = 0; l < numLabels; ++l) {
1227         PetscInt val;
1228         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1229         if (val >= 0) {
1230           color     = lcolors[l % numLColors];
1231           isLabeled = PETSC_TRUE;
1232           break;
1233         }
1234       }
1235       if (drawNumbers[0]) {
1236         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1237       } else if (drawColors[0]) {
1238         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1239       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1240     }
1241     PetscCall(VecRestoreArray(coordinates, &coords));
1242     PetscCall(PetscViewerFlush(viewer));
1243     /* Plot edges */
1244     if (plotEdges) {
1245       PetscCall(VecGetArray(coordinates, &coords));
1246       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1247       for (e = eStart; e < eEnd; ++e) {
1248         const PetscInt *cone;
1249         PetscInt        coneSize, offA, offB, dof, d;
1250 
1251         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1252         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1253         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1254         PetscCall(DMPlexGetCone(dm, e, &cone));
1255         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1256         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1257         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1258         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1259         for (d = 0; d < dof; ++d) {
1260           tcoords[d] = (double)(0.5 * scale * PetscRealPart(coords[offA + d] + coords[offB + d]));
1261           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1262         }
1263         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1264         if (dim == 3) {
1265           PetscReal tmp = tcoords[1];
1266           tcoords[1]    = tcoords[2];
1267           tcoords[2]    = -tmp;
1268         }
1269         for (d = 0; d < dof; ++d) {
1270           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1271           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1272         }
1273         if (drawHasse) color = colors[1 % numColors];
1274         else color = colors[rank % numColors];
1275         for (l = 0; l < numLabels; ++l) {
1276           PetscInt val;
1277           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1278           if (val >= 0) {
1279             color = lcolors[l % numLColors];
1280             break;
1281           }
1282         }
1283         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1284       }
1285       PetscCall(VecRestoreArray(coordinates, &coords));
1286       PetscCall(PetscViewerFlush(viewer));
1287       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1288     }
1289     /* Plot cells */
1290     if (dim == 3 || !drawNumbers[1]) {
1291       for (e = eStart; e < eEnd; ++e) {
1292         const PetscInt *cone;
1293 
1294         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1295         color = colors[rank % numColors];
1296         for (l = 0; l < numLabels; ++l) {
1297           PetscInt val;
1298           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1299           if (val >= 0) {
1300             color = lcolors[l % numLColors];
1301             break;
1302           }
1303         }
1304         PetscCall(DMPlexGetCone(dm, e, &cone));
1305         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1306       }
1307     } else {
1308       DMPolytopeType ct;
1309 
1310       /* Drawing a 2D polygon */
1311       for (c = cStart; c < cEnd; ++c) {
1312         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1313         PetscCall(DMPlexGetCellType(dm, c, &ct));
1314         if (DMPolytopeTypeIsHybrid(ct)) {
1315           const PetscInt *cone;
1316           PetscInt        coneSize, e;
1317 
1318           PetscCall(DMPlexGetCone(dm, c, &cone));
1319           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1320           for (e = 0; e < coneSize; ++e) {
1321             const PetscInt *econe;
1322 
1323             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1324             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));
1325           }
1326         } else {
1327           PetscInt *closure = NULL;
1328           PetscInt  closureSize, Nv = 0, v;
1329 
1330           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1331           for (p = 0; p < closureSize * 2; p += 2) {
1332             const PetscInt point = closure[p];
1333 
1334             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1335           }
1336           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1337           for (v = 0; v <= Nv; ++v) {
1338             const PetscInt vertex = closure[v % Nv];
1339 
1340             if (v > 0) {
1341               if (plotEdges) {
1342                 const PetscInt *edge;
1343                 PetscInt        endpoints[2], ne;
1344 
1345                 endpoints[0] = closure[v - 1];
1346                 endpoints[1] = vertex;
1347                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1348                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1349                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1350                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1351               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1352             }
1353             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1354           }
1355           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1356           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1357         }
1358       }
1359     }
1360     for (c = cStart; c < cEnd; ++c) {
1361       double             ccoords[3] = {0.0, 0.0, 0.0};
1362       PetscBool          isLabeled  = PETSC_FALSE;
1363       PetscScalar       *cellCoords = NULL;
1364       const PetscScalar *array;
1365       PetscInt           numCoords, cdim, d;
1366       PetscBool          isDG;
1367 
1368       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1369       PetscCall(DMGetCoordinateDim(dm, &cdim));
1370       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1371       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1372       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1373       for (p = 0; p < numCoords / cdim; ++p) {
1374         for (d = 0; d < cdim; ++d) {
1375           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1376           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1377         }
1378         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1379         if (cdim == 3) {
1380           PetscReal tmp = tcoords[1];
1381           tcoords[1]    = tcoords[2];
1382           tcoords[2]    = -tmp;
1383         }
1384         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1385       }
1386       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1387       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1388       for (d = 0; d < cdim; ++d) {
1389         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1390         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)ccoords[d]));
1391       }
1392       if (drawHasse) color = colors[depth % numColors];
1393       else color = colors[rank % numColors];
1394       for (l = 0; l < numLabels; ++l) {
1395         PetscInt val;
1396         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1397         if (val >= 0) {
1398           color     = lcolors[l % numLColors];
1399           isLabeled = PETSC_TRUE;
1400           break;
1401         }
1402       }
1403       if (drawNumbers[dim]) {
1404         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1405       } else if (drawColors[dim]) {
1406         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1407       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1408     }
1409     if (drawHasse) {
1410       int height = 0;
1411 
1412       color = colors[depth % numColors];
1413       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1414       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1415       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1416       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,%d) {\\c};\n", rank, color, height++));
1417       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1418 
1419       if (depth > 2) {
1420         color = colors[1 % numColors];
1421         PetscCall(PetscViewerASCIIPrintf(viewer, "%% Faces\n"));
1422         PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\f in {\\fStart,...,\\fEnd}\n"));
1423         PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1424         PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\f_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\fShift+\\f-\\fStart,%d) {\\f};\n", rank, color, height++));
1425         PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1426       }
1427 
1428       color = colors[1 % numColors];
1429       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1430       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1431       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1432       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,%d) {\\e};\n", rank, color, height++));
1433       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1434 
1435       color = colors[0 % numColors];
1436       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1437       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1438       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1439       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,%d) {\\v};\n", rank, color, height++));
1440       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1441 
1442       for (p = pStart; p < pEnd; ++p) {
1443         const PetscInt *cone;
1444         PetscInt        coneSize, cp;
1445 
1446         PetscCall(DMPlexGetCone(dm, p, &cone));
1447         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1448         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1449       }
1450     }
1451     PetscCall(PetscViewerFlush(viewer));
1452     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1453     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1454     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1455     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1456     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1457     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1458     PetscCall(PetscFree3(names, colors, lcolors));
1459     PetscCall(PetscBTDestroy(&wp));
1460   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1461     Vec                    cown, acown;
1462     VecScatter             sct;
1463     ISLocalToGlobalMapping g2l;
1464     IS                     gid, acis;
1465     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1466     MPI_Group              ggroup, ngroup;
1467     PetscScalar           *array, nid;
1468     const PetscInt        *idxs;
1469     PetscInt              *idxs2, *start, *adjacency, *work;
1470     PetscInt64             lm[3], gm[3];
1471     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1472     PetscMPIInt            d1, d2, rank;
1473 
1474     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1475     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1476 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1477     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1478 #endif
1479     if (ncomm != MPI_COMM_NULL) {
1480       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1481       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1482       d1 = 0;
1483       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1484       nid = d2;
1485       PetscCallMPI(MPI_Group_free(&ggroup));
1486       PetscCallMPI(MPI_Group_free(&ngroup));
1487       PetscCallMPI(MPI_Comm_free(&ncomm));
1488     } else nid = 0.0;
1489 
1490     /* Get connectivity */
1491     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1492     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1493 
1494     /* filter overlapped local cells */
1495     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1496     PetscCall(ISGetIndices(gid, &idxs));
1497     PetscCall(ISGetLocalSize(gid, &cum));
1498     PetscCall(PetscMalloc1(cum, &idxs2));
1499     for (c = cStart, cum = 0; c < cEnd; c++) {
1500       if (idxs[c - cStart] < 0) continue;
1501       idxs2[cum++] = idxs[c - cStart];
1502     }
1503     PetscCall(ISRestoreIndices(gid, &idxs));
1504     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1505     PetscCall(ISDestroy(&gid));
1506     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1507 
1508     /* support for node-aware cell locality */
1509     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1510     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1511     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1512     PetscCall(VecGetArray(cown, &array));
1513     for (c = 0; c < numVertices; c++) array[c] = nid;
1514     PetscCall(VecRestoreArray(cown, &array));
1515     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1516     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1517     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1518     PetscCall(ISDestroy(&acis));
1519     PetscCall(VecScatterDestroy(&sct));
1520     PetscCall(VecDestroy(&cown));
1521 
1522     /* compute edgeCut */
1523     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1524     PetscCall(PetscMalloc1(cum, &work));
1525     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1526     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1527     PetscCall(ISDestroy(&gid));
1528     PetscCall(VecGetArray(acown, &array));
1529     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1530       PetscInt totl;
1531 
1532       totl = start[c + 1] - start[c];
1533       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1534       for (i = 0; i < totl; i++) {
1535         if (work[i] < 0) {
1536           ect += 1;
1537           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1538         }
1539       }
1540     }
1541     PetscCall(PetscFree(work));
1542     PetscCall(VecRestoreArray(acown, &array));
1543     lm[0] = numVertices > 0 ? numVertices : PETSC_MAX_INT;
1544     lm[1] = -numVertices;
1545     PetscCall(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1546     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT, -((double)gm[1]) / ((double)gm[0]), -(PetscInt)gm[1], (PetscInt)gm[0]));
1547     lm[0] = ect;                     /* edgeCut */
1548     lm[1] = ectn;                    /* node-aware edgeCut */
1549     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1550     PetscCall(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1551     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt_FMT ")\n", (PetscInt)gm[2]));
1552 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1553     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), gm[0] ? ((double)gm[1]) / ((double)gm[0]) : 1.));
1554 #else
1555     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), 0.0));
1556 #endif
1557     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1558     PetscCall(PetscFree(start));
1559     PetscCall(PetscFree(adjacency));
1560     PetscCall(VecDestroy(&acown));
1561   } else {
1562     const char    *name;
1563     PetscInt      *sizes, *hybsizes, *ghostsizes;
1564     PetscInt       locDepth, depth, cellHeight, dim, d;
1565     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1566     PetscInt       numLabels, l, maxSize = 17;
1567     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1568     MPI_Comm       comm;
1569     PetscMPIInt    size, rank;
1570 
1571     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1572     PetscCallMPI(MPI_Comm_size(comm, &size));
1573     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1574     PetscCall(DMGetDimension(dm, &dim));
1575     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1576     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1577     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1578     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1579     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1580     PetscCall(DMPlexGetDepth(dm, &locDepth));
1581     PetscCall(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1582     PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &gcStart, &gcEnd));
1583     gcNum = gcEnd - gcStart;
1584     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1585     else PetscCall(PetscCalloc3(3, &sizes, 3, &hybsizes, 3, &ghostsizes));
1586     for (d = 0; d <= depth; d++) {
1587       PetscInt Nc[2] = {0, 0}, ict;
1588 
1589       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1590       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1591       ict = ct0;
1592       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1593       ct0 = (DMPolytopeType)ict;
1594       for (p = pStart; p < pEnd; ++p) {
1595         DMPolytopeType ct;
1596 
1597         PetscCall(DMPlexGetCellType(dm, p, &ct));
1598         if (ct == ct0) ++Nc[0];
1599         else ++Nc[1];
1600       }
1601       if (size < maxSize) {
1602         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes, 1, MPIU_INT, 0, comm));
1603         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1604         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1605         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1606         for (p = 0; p < size; ++p) {
1607           if (rank == 0) {
1608             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p] + hybsizes[p]));
1609             if (hybsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1610             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1611           }
1612         }
1613       } else {
1614         PetscInt locMinMax[2];
1615 
1616         locMinMax[0] = Nc[0] + Nc[1];
1617         locMinMax[1] = Nc[0] + Nc[1];
1618         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1619         locMinMax[0] = Nc[1];
1620         locMinMax[1] = Nc[1];
1621         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1622         if (d == depth) {
1623           locMinMax[0] = gcNum;
1624           locMinMax[1] = gcNum;
1625           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1626         }
1627         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1628         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1629         if (hybsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1630         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1631       }
1632       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1633     }
1634     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1635     {
1636       const PetscReal *maxCell;
1637       const PetscReal *L;
1638       PetscBool        localized;
1639 
1640       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1641       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1642       if (L || localized) {
1643         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1644         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1645         if (L) {
1646           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1647           for (d = 0; d < dim; ++d) {
1648             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1649             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1650           }
1651           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1652         }
1653         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1654         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1655       }
1656     }
1657     PetscCall(DMGetNumLabels(dm, &numLabels));
1658     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1659     for (l = 0; l < numLabels; ++l) {
1660       DMLabel     label;
1661       const char *name;
1662       PetscInt   *values;
1663       PetscInt    numValues, v;
1664 
1665       PetscCall(DMGetLabelName(dm, l, &name));
1666       PetscCall(DMGetLabel(dm, name, &label));
1667       PetscCall(DMLabelGetNumValues(label, &numValues));
1668       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1669 
1670       { // Extract array of DMLabel values so it can be sorted
1671         IS              is_values;
1672         const PetscInt *is_values_local = NULL;
1673 
1674         PetscCall(DMLabelGetValueIS(label, &is_values));
1675         PetscCall(ISGetIndices(is_values, &is_values_local));
1676         PetscCall(PetscMalloc1(numValues, &values));
1677         PetscCall(PetscArraycpy(values, is_values_local, numValues));
1678         PetscCall(PetscSortInt(numValues, values));
1679         PetscCall(ISRestoreIndices(is_values, &is_values_local));
1680         PetscCall(ISDestroy(&is_values));
1681       }
1682       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1683       for (v = 0; v < numValues; ++v) {
1684         PetscInt size;
1685 
1686         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1687         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1688         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1689       }
1690       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1691       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1692       PetscCall(PetscFree(values));
1693     }
1694     {
1695       char    **labelNames;
1696       PetscInt  Nl = numLabels;
1697       PetscBool flg;
1698 
1699       PetscCall(PetscMalloc1(Nl, &labelNames));
1700       PetscCall(PetscOptionsGetStringArray(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1701       for (l = 0; l < Nl; ++l) {
1702         DMLabel label;
1703 
1704         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1705         if (flg) {
1706           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1707           PetscCall(DMLabelView(label, viewer));
1708         }
1709         PetscCall(PetscFree(labelNames[l]));
1710       }
1711       PetscCall(PetscFree(labelNames));
1712     }
1713     /* If no fields are specified, people do not want to see adjacency */
1714     if (dm->Nf) {
1715       PetscInt f;
1716 
1717       for (f = 0; f < dm->Nf; ++f) {
1718         const char *name;
1719 
1720         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1721         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1722         PetscCall(PetscViewerASCIIPushTab(viewer));
1723         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1724         if (dm->fields[f].adjacency[0]) {
1725           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1726           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1727         } else {
1728           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1729           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1730         }
1731         PetscCall(PetscViewerASCIIPopTab(viewer));
1732       }
1733     }
1734     PetscCall(DMGetCoarseDM(dm, &cdm));
1735     if (cdm) {
1736       PetscCall(PetscViewerASCIIPushTab(viewer));
1737       PetscCall(PetscViewerASCIIPrintf(viewer, "Defined by transform from:\n"));
1738       PetscCall(DMPlexView_Ascii(cdm, viewer));
1739       PetscCall(PetscViewerASCIIPopTab(viewer));
1740     }
1741   }
1742   PetscFunctionReturn(PETSC_SUCCESS);
1743 }
1744 
1745 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1746 {
1747   DMPolytopeType ct;
1748   PetscMPIInt    rank;
1749   PetscInt       cdim;
1750 
1751   PetscFunctionBegin;
1752   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1753   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1754   PetscCall(DMGetCoordinateDim(dm, &cdim));
1755   switch (ct) {
1756   case DM_POLYTOPE_SEGMENT:
1757   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1758     switch (cdim) {
1759     case 1: {
1760       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1761       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1762 
1763       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, PETSC_DRAW_BLACK));
1764       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, PETSC_DRAW_BLACK));
1765       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, PETSC_DRAW_BLACK));
1766     } break;
1767     case 2: {
1768       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1769       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1770       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1771 
1772       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1773       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));
1774       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));
1775     } break;
1776     default:
1777       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1778     }
1779     break;
1780   case DM_POLYTOPE_TRIANGLE:
1781     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));
1782     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1783     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1784     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1785     break;
1786   case DM_POLYTOPE_QUADRILATERAL:
1787     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));
1788     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));
1789     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1790     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1791     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1792     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1793     break;
1794   case DM_POLYTOPE_SEG_PRISM_TENSOR:
1795     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));
1796     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));
1797     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1798     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1799     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1800     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1801     break;
1802   case DM_POLYTOPE_FV_GHOST:
1803     break;
1804   default:
1805     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1806   }
1807   PetscFunctionReturn(PETSC_SUCCESS);
1808 }
1809 
1810 static PetscErrorCode DrawPolygon_Private(DM dm, PetscDraw draw, PetscInt cell, PetscInt Nv, const PetscReal refVertices[], const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1811 {
1812   PetscReal   centroid[2] = {0., 0.};
1813   PetscMPIInt rank;
1814   PetscInt    fillColor;
1815 
1816   PetscFunctionBegin;
1817   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1818   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1819   for (PetscInt v = 0; v < Nv; ++v) {
1820     centroid[0] += PetscRealPart(coords[v * 2 + 0]) / Nv;
1821     centroid[1] += PetscRealPart(coords[v * 2 + 1]) / Nv;
1822   }
1823   for (PetscInt e = 0; e < Nv; ++e) {
1824     refCoords[0] = refVertices[e * 2 + 0];
1825     refCoords[1] = refVertices[e * 2 + 1];
1826     for (PetscInt d = 1; d <= edgeDiv; ++d) {
1827       refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % Nv * 2 + 0] - refCoords[0]) * d / edgeDiv;
1828       refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % Nv * 2 + 1] - refCoords[1]) * d / edgeDiv;
1829     }
1830     PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1831     for (PetscInt d = 0; d < edgeDiv; ++d) {
1832       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));
1833       PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1834     }
1835   }
1836   PetscFunctionReturn(PETSC_SUCCESS);
1837 }
1838 
1839 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1840 {
1841   DMPolytopeType ct;
1842 
1843   PetscFunctionBegin;
1844   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1845   switch (ct) {
1846   case DM_POLYTOPE_TRIANGLE: {
1847     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1848 
1849     PetscCall(DrawPolygon_Private(dm, draw, cell, 3, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1850   } break;
1851   case DM_POLYTOPE_QUADRILATERAL: {
1852     PetscReal refVertices[8] = {-1., -1., 1., -1., 1., 1., -1., 1.};
1853 
1854     PetscCall(DrawPolygon_Private(dm, draw, cell, 4, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1855   } break;
1856   default:
1857     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1858   }
1859   PetscFunctionReturn(PETSC_SUCCESS);
1860 }
1861 
1862 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1863 {
1864   PetscDraw    draw;
1865   DM           cdm;
1866   PetscSection coordSection;
1867   Vec          coordinates;
1868   PetscReal    xyl[3], xyr[3];
1869   PetscReal   *refCoords, *edgeCoords;
1870   PetscBool    isnull, drawAffine;
1871   PetscInt     dim, vStart, vEnd, cStart, cEnd, c, cDegree, edgeDiv;
1872 
1873   PetscFunctionBegin;
1874   PetscCall(DMGetCoordinateDim(dm, &dim));
1875   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1876   PetscCall(DMGetCoordinateDegree_Internal(dm, &cDegree));
1877   drawAffine = cDegree > 1 ? PETSC_FALSE : PETSC_TRUE;
1878   edgeDiv    = cDegree + 1;
1879   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1880   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1881   PetscCall(DMGetCoordinateDM(dm, &cdm));
1882   PetscCall(DMGetLocalSection(cdm, &coordSection));
1883   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1884   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1885   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1886 
1887   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1888   PetscCall(PetscDrawIsNull(draw, &isnull));
1889   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
1890   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1891 
1892   PetscCall(DMGetBoundingBox(dm, xyl, xyr));
1893   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1894   PetscCall(PetscDrawClear(draw));
1895 
1896   for (c = cStart; c < cEnd; ++c) {
1897     PetscScalar       *coords = NULL;
1898     const PetscScalar *coords_arr;
1899     PetscInt           numCoords;
1900     PetscBool          isDG;
1901 
1902     PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1903     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1904     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1905     PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1906   }
1907   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1908   PetscCall(PetscDrawFlush(draw));
1909   PetscCall(PetscDrawPause(draw));
1910   PetscCall(PetscDrawSave(draw));
1911   PetscFunctionReturn(PETSC_SUCCESS);
1912 }
1913 
1914 static PetscErrorCode DMPlexCreateHighOrderSurrogate_Internal(DM dm, DM *hdm)
1915 {
1916   DM           odm = dm, rdm = dm, cdm;
1917   PetscFE      fe;
1918   PetscSpace   sp;
1919   PetscClassId id;
1920   PetscInt     degree;
1921   PetscBool    hoView = PETSC_TRUE;
1922 
1923   PetscFunctionBegin;
1924   PetscObjectOptionsBegin((PetscObject)dm);
1925   PetscCall(PetscOptionsBool("-dm_plex_high_order_view", "Subsample to view meshes with high order coordinates", "DMPlexCreateHighOrderSurrogate_Internal", hoView, &hoView, NULL));
1926   PetscOptionsEnd();
1927   PetscCall(PetscObjectReference((PetscObject)dm));
1928   *hdm = dm;
1929   if (!hoView) PetscFunctionReturn(PETSC_SUCCESS);
1930   PetscCall(DMGetCoordinateDM(dm, &cdm));
1931   PetscCall(DMGetField(cdm, 0, NULL, (PetscObject *)&fe));
1932   PetscCall(PetscObjectGetClassId((PetscObject)fe, &id));
1933   if (id != PETSCFE_CLASSID) PetscFunctionReturn(PETSC_SUCCESS);
1934   PetscCall(PetscFEGetBasisSpace(fe, &sp));
1935   PetscCall(PetscSpaceGetDegree(sp, &degree, NULL));
1936   for (PetscInt r = 0, rd = PetscCeilReal(((PetscReal)degree) / 2.); r < (PetscInt)PetscCeilReal(PetscLog2Real(degree)); ++r, rd = PetscCeilReal(((PetscReal)rd) / 2.)) {
1937     DM  cdm, rcdm;
1938     Mat In;
1939     Vec cl, rcl;
1940 
1941     PetscCall(DMRefine(odm, PetscObjectComm((PetscObject)odm), &rdm));
1942     PetscCall(DMPlexCreateCoordinateSpace(rdm, rd, PETSC_FALSE, NULL));
1943     PetscCall(PetscObjectSetName((PetscObject)rdm, "Refined Mesh with Linear Coordinates"));
1944     PetscCall(DMGetCoordinateDM(odm, &cdm));
1945     PetscCall(DMGetCoordinateDM(rdm, &rcdm));
1946     PetscCall(DMGetCoordinatesLocal(odm, &cl));
1947     PetscCall(DMGetCoordinatesLocal(rdm, &rcl));
1948     PetscCall(DMSetCoarseDM(rcdm, cdm));
1949     PetscCall(DMCreateInterpolation(cdm, rcdm, &In, NULL));
1950     PetscCall(MatMult(In, cl, rcl));
1951     PetscCall(MatDestroy(&In));
1952     PetscCall(DMSetCoordinatesLocal(rdm, rcl));
1953     PetscCall(DMDestroy(&odm));
1954     odm = rdm;
1955   }
1956   *hdm = rdm;
1957   PetscFunctionReturn(PETSC_SUCCESS);
1958 }
1959 
1960 #if defined(PETSC_HAVE_EXODUSII)
1961   #include <exodusII.h>
1962   #include <petscviewerexodusii.h>
1963 #endif
1964 
1965 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1966 {
1967   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns;
1968   char      name[PETSC_MAX_PATH_LEN];
1969 
1970   PetscFunctionBegin;
1971   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1972   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1973   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
1974   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
1975   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1976   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
1977   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
1978   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
1979   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
1980   if (iascii) {
1981     PetscViewerFormat format;
1982     PetscCall(PetscViewerGetFormat(viewer, &format));
1983     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1984     else PetscCall(DMPlexView_Ascii(dm, viewer));
1985   } else if (ishdf5) {
1986 #if defined(PETSC_HAVE_HDF5)
1987     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1988 #else
1989     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1990 #endif
1991   } else if (isvtk) {
1992     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
1993   } else if (isdraw) {
1994     DM hdm;
1995 
1996     PetscCall(DMPlexCreateHighOrderSurrogate_Internal(dm, &hdm));
1997     PetscCall(DMPlexView_Draw(hdm, viewer));
1998     PetscCall(DMDestroy(&hdm));
1999   } else if (isglvis) {
2000     PetscCall(DMPlexView_GLVis(dm, viewer));
2001 #if defined(PETSC_HAVE_EXODUSII)
2002   } else if (isexodus) {
2003     /*
2004       exodusII requires that all sets be part of exactly one cell set.
2005       If the dm does not have a "Cell Sets" label defined, we create one
2006       with ID 1, containing all cells.
2007       Note that if the Cell Sets label is defined but does not cover all cells,
2008       we may still have a problem. This should probably be checked here or in the viewer;
2009     */
2010     PetscInt numCS;
2011     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
2012     if (!numCS) {
2013       PetscInt cStart, cEnd, c;
2014       PetscCall(DMCreateLabel(dm, "Cell Sets"));
2015       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
2016       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
2017     }
2018     PetscCall(DMView_PlexExodusII(dm, viewer));
2019 #endif
2020 #if defined(PETSC_HAVE_CGNS)
2021   } else if (iscgns) {
2022     PetscCall(DMView_PlexCGNS(dm, viewer));
2023 #endif
2024   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
2025   /* Optionally view the partition */
2026   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
2027   if (flg) {
2028     Vec ranks;
2029     PetscCall(DMPlexCreateRankField(dm, &ranks));
2030     PetscCall(VecView(ranks, viewer));
2031     PetscCall(VecDestroy(&ranks));
2032   }
2033   /* Optionally view a label */
2034   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
2035   if (flg) {
2036     DMLabel label;
2037     Vec     val;
2038 
2039     PetscCall(DMGetLabel(dm, name, &label));
2040     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
2041     PetscCall(DMPlexCreateLabelField(dm, label, &val));
2042     PetscCall(VecView(val, viewer));
2043     PetscCall(VecDestroy(&val));
2044   }
2045   PetscFunctionReturn(PETSC_SUCCESS);
2046 }
2047 
2048 /*@
2049   DMPlexTopologyView - Saves a `DMPLEX` topology into a file
2050 
2051   Collective
2052 
2053   Input Parameters:
2054 + dm     - The `DM` whose topology is to be saved
2055 - viewer - The `PetscViewer` to save it in
2056 
2057   Level: advanced
2058 
2059 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`, `PetscViewer`
2060 @*/
2061 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
2062 {
2063   PetscBool ishdf5;
2064 
2065   PetscFunctionBegin;
2066   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2067   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2068   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2069   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
2070   if (ishdf5) {
2071 #if defined(PETSC_HAVE_HDF5)
2072     PetscViewerFormat format;
2073     PetscCall(PetscViewerGetFormat(viewer, &format));
2074     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2075       IS globalPointNumbering;
2076 
2077       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2078       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
2079       PetscCall(ISDestroy(&globalPointNumbering));
2080     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2081 #else
2082     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2083 #endif
2084   }
2085   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
2086   PetscFunctionReturn(PETSC_SUCCESS);
2087 }
2088 
2089 /*@
2090   DMPlexCoordinatesView - Saves `DMPLEX` coordinates into a file
2091 
2092   Collective
2093 
2094   Input Parameters:
2095 + dm     - The `DM` whose coordinates are to be saved
2096 - viewer - The `PetscViewer` for saving
2097 
2098   Level: advanced
2099 
2100 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`, `PetscViewer`
2101 @*/
2102 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
2103 {
2104   PetscBool ishdf5;
2105 
2106   PetscFunctionBegin;
2107   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2108   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2109   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2110   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2111   if (ishdf5) {
2112 #if defined(PETSC_HAVE_HDF5)
2113     PetscViewerFormat format;
2114     PetscCall(PetscViewerGetFormat(viewer, &format));
2115     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2116       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
2117     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2118 #else
2119     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2120 #endif
2121   }
2122   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2123   PetscFunctionReturn(PETSC_SUCCESS);
2124 }
2125 
2126 /*@
2127   DMPlexLabelsView - Saves `DMPLEX` labels into a file
2128 
2129   Collective
2130 
2131   Input Parameters:
2132 + dm     - The `DM` whose labels are to be saved
2133 - viewer - The `PetscViewer` for saving
2134 
2135   Level: advanced
2136 
2137 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`, `PetscViewer`
2138 @*/
2139 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
2140 {
2141   PetscBool ishdf5;
2142 
2143   PetscFunctionBegin;
2144   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2145   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2146   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2147   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
2148   if (ishdf5) {
2149 #if defined(PETSC_HAVE_HDF5)
2150     IS                globalPointNumbering;
2151     PetscViewerFormat format;
2152 
2153     PetscCall(PetscViewerGetFormat(viewer, &format));
2154     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2155       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2156       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
2157       PetscCall(ISDestroy(&globalPointNumbering));
2158     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2159 #else
2160     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2161 #endif
2162   }
2163   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
2164   PetscFunctionReturn(PETSC_SUCCESS);
2165 }
2166 
2167 /*@
2168   DMPlexSectionView - Saves a section associated with a `DMPLEX`
2169 
2170   Collective
2171 
2172   Input Parameters:
2173 + dm        - The `DM` that contains the topology on which the section to be saved is defined
2174 . viewer    - The `PetscViewer` for saving
2175 - sectiondm - The `DM` that contains the section to be saved, can be `NULL`
2176 
2177   Level: advanced
2178 
2179   Notes:
2180   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.
2181 
2182   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 (or in case `sectiondm` is `NULL`) 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.
2183 
2184 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`, `PetscViewer`
2185 @*/
2186 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
2187 {
2188   PetscBool ishdf5;
2189 
2190   PetscFunctionBegin;
2191   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2192   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2193   if (!sectiondm) sectiondm = dm;
2194   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2195   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2196   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
2197   if (ishdf5) {
2198 #if defined(PETSC_HAVE_HDF5)
2199     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
2200 #else
2201     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2202 #endif
2203   }
2204   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
2205   PetscFunctionReturn(PETSC_SUCCESS);
2206 }
2207 
2208 /*@
2209   DMPlexGlobalVectorView - Saves a global vector
2210 
2211   Collective
2212 
2213   Input Parameters:
2214 + dm        - The `DM` that represents the topology
2215 . viewer    - The `PetscViewer` to save data with
2216 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2217 - vec       - The global vector to be saved
2218 
2219   Level: advanced
2220 
2221   Notes:
2222   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 (or in case `sectiondm` is `NULL`) 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.
2223 
2224   Calling sequence:
2225 .vb
2226        DMCreate(PETSC_COMM_WORLD, &dm);
2227        DMSetType(dm, DMPLEX);
2228        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2229        DMClone(dm, &sectiondm);
2230        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2231        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2232        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2233        PetscSectionSetChart(section, pStart, pEnd);
2234        PetscSectionSetUp(section);
2235        DMSetLocalSection(sectiondm, section);
2236        PetscSectionDestroy(&section);
2237        DMGetGlobalVector(sectiondm, &vec);
2238        PetscObjectSetName((PetscObject)vec, "vec_name");
2239        DMPlexTopologyView(dm, viewer);
2240        DMPlexSectionView(dm, viewer, sectiondm);
2241        DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
2242        DMRestoreGlobalVector(sectiondm, &vec);
2243        DMDestroy(&sectiondm);
2244        DMDestroy(&dm);
2245 .ve
2246 
2247 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2248 @*/
2249 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2250 {
2251   PetscBool ishdf5;
2252 
2253   PetscFunctionBegin;
2254   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2255   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2256   if (!sectiondm) sectiondm = dm;
2257   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2258   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2259   /* Check consistency */
2260   {
2261     PetscSection section;
2262     PetscBool    includesConstraints;
2263     PetscInt     m, m1;
2264 
2265     PetscCall(VecGetLocalSize(vec, &m1));
2266     PetscCall(DMGetGlobalSection(sectiondm, &section));
2267     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2268     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2269     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2270     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2271   }
2272   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2273   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2274   if (ishdf5) {
2275 #if defined(PETSC_HAVE_HDF5)
2276     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2277 #else
2278     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2279 #endif
2280   }
2281   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2282   PetscFunctionReturn(PETSC_SUCCESS);
2283 }
2284 
2285 /*@
2286   DMPlexLocalVectorView - Saves a local vector
2287 
2288   Collective
2289 
2290   Input Parameters:
2291 + dm        - The `DM` that represents the topology
2292 . viewer    - The `PetscViewer` to save data with
2293 . sectiondm - The `DM` that contains the local section on which `vec` is defined, can be `NULL`
2294 - vec       - The local vector to be saved
2295 
2296   Level: advanced
2297 
2298   Note:
2299   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 (or in case `sectiondm` is `NULL`) 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.
2300 
2301   Calling sequence:
2302 .vb
2303        DMCreate(PETSC_COMM_WORLD, &dm);
2304        DMSetType(dm, DMPLEX);
2305        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2306        DMClone(dm, &sectiondm);
2307        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2308        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2309        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2310        PetscSectionSetChart(section, pStart, pEnd);
2311        PetscSectionSetUp(section);
2312        DMSetLocalSection(sectiondm, section);
2313        DMGetLocalVector(sectiondm, &vec);
2314        PetscObjectSetName((PetscObject)vec, "vec_name");
2315        DMPlexTopologyView(dm, viewer);
2316        DMPlexSectionView(dm, viewer, sectiondm);
2317        DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2318        DMRestoreLocalVector(sectiondm, &vec);
2319        DMDestroy(&sectiondm);
2320        DMDestroy(&dm);
2321 .ve
2322 
2323 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2324 @*/
2325 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2326 {
2327   PetscBool ishdf5;
2328 
2329   PetscFunctionBegin;
2330   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2331   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2332   if (!sectiondm) sectiondm = dm;
2333   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2334   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2335   /* Check consistency */
2336   {
2337     PetscSection section;
2338     PetscBool    includesConstraints;
2339     PetscInt     m, m1;
2340 
2341     PetscCall(VecGetLocalSize(vec, &m1));
2342     PetscCall(DMGetLocalSection(sectiondm, &section));
2343     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2344     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2345     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2346     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2347   }
2348   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2349   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2350   if (ishdf5) {
2351 #if defined(PETSC_HAVE_HDF5)
2352     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
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_LocalVectorView, viewer, 0, 0, 0));
2358   PetscFunctionReturn(PETSC_SUCCESS);
2359 }
2360 
2361 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2362 {
2363   PetscBool ishdf5;
2364 
2365   PetscFunctionBegin;
2366   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2367   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2368   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2369   if (ishdf5) {
2370 #if defined(PETSC_HAVE_HDF5)
2371     PetscViewerFormat format;
2372     PetscCall(PetscViewerGetFormat(viewer, &format));
2373     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2374       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2375     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2376       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2377     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2378     PetscFunctionReturn(PETSC_SUCCESS);
2379 #else
2380     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2381 #endif
2382   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2383 }
2384 
2385 /*@
2386   DMPlexTopologyLoad - Loads a topology into a `DMPLEX`
2387 
2388   Collective
2389 
2390   Input Parameters:
2391 + dm     - The `DM` into which the topology is loaded
2392 - viewer - The `PetscViewer` for the saved topology
2393 
2394   Output Parameter:
2395 . 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;
2396   `NULL` if unneeded
2397 
2398   Level: advanced
2399 
2400 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2401           `PetscViewer`, `PetscSF`
2402 @*/
2403 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2404 {
2405   PetscBool ishdf5;
2406 
2407   PetscFunctionBegin;
2408   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2409   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2410   if (globalToLocalPointSF) PetscAssertPointer(globalToLocalPointSF, 3);
2411   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2412   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2413   if (ishdf5) {
2414 #if defined(PETSC_HAVE_HDF5)
2415     PetscViewerFormat format;
2416     PetscCall(PetscViewerGetFormat(viewer, &format));
2417     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2418       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2419     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2420 #else
2421     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2422 #endif
2423   }
2424   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2425   PetscFunctionReturn(PETSC_SUCCESS);
2426 }
2427 
2428 /*@
2429   DMPlexCoordinatesLoad - Loads coordinates into a `DMPLEX`
2430 
2431   Collective
2432 
2433   Input Parameters:
2434 + dm                   - The `DM` into which the coordinates are loaded
2435 . viewer               - The `PetscViewer` for the saved coordinates
2436 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2437 
2438   Level: advanced
2439 
2440 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2441           `PetscSF`, `PetscViewer`
2442 @*/
2443 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2444 {
2445   PetscBool ishdf5;
2446 
2447   PetscFunctionBegin;
2448   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2449   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2450   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2451   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2452   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2453   if (ishdf5) {
2454 #if defined(PETSC_HAVE_HDF5)
2455     PetscViewerFormat format;
2456     PetscCall(PetscViewerGetFormat(viewer, &format));
2457     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2458       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2459     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2460 #else
2461     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2462 #endif
2463   }
2464   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2465   PetscFunctionReturn(PETSC_SUCCESS);
2466 }
2467 
2468 /*@
2469   DMPlexLabelsLoad - Loads labels into a `DMPLEX`
2470 
2471   Collective
2472 
2473   Input Parameters:
2474 + dm                   - The `DM` into which the labels are loaded
2475 . viewer               - The `PetscViewer` for the saved labels
2476 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading `dm` from viewer
2477 
2478   Level: advanced
2479 
2480   Note:
2481   The `PetscSF` argument must not be `NULL` if the `DM` is distributed, otherwise an error occurs.
2482 
2483 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2484           `PetscSF`, `PetscViewer`
2485 @*/
2486 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2487 {
2488   PetscBool ishdf5;
2489 
2490   PetscFunctionBegin;
2491   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2492   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2493   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2494   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2495   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2496   if (ishdf5) {
2497 #if defined(PETSC_HAVE_HDF5)
2498     PetscViewerFormat format;
2499 
2500     PetscCall(PetscViewerGetFormat(viewer, &format));
2501     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2502       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2503     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2504 #else
2505     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2506 #endif
2507   }
2508   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2509   PetscFunctionReturn(PETSC_SUCCESS);
2510 }
2511 
2512 /*@
2513   DMPlexSectionLoad - Loads section into a `DMPLEX`
2514 
2515   Collective
2516 
2517   Input Parameters:
2518 + dm                   - The `DM` that represents the topology
2519 . viewer               - The `PetscViewer` that represents the on-disk section (sectionA)
2520 . sectiondm            - The `DM` into which the on-disk section (sectionA) is migrated, can be `NULL`
2521 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad(`) when loading dm from viewer
2522 
2523   Output Parameters:
2524 + 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)
2525 - 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)
2526 
2527   Level: advanced
2528 
2529   Notes:
2530   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.
2531 
2532   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 (or in case `sectiondm` is `NULL`) 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.
2533 
2534   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.
2535 
2536   Example using 2 processes:
2537 .vb
2538   NX (number of points on dm): 4
2539   sectionA                   : the on-disk section
2540   vecA                       : a vector associated with sectionA
2541   sectionB                   : sectiondm's local section constructed in this function
2542   vecB (local)               : a vector associated with sectiondm's local section
2543   vecB (global)              : a vector associated with sectiondm's global section
2544 
2545                                      rank 0    rank 1
2546   vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2547   sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2548   sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2549   sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2550   [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2551   sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2552   sectionB->atlasDof             :     1 0 1 | 1 3
2553   sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2554   vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2555   vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2556 .ve
2557   where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2558 
2559 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`, `PetscSF`, `PetscViewer`
2560 @*/
2561 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
2562 {
2563   PetscBool ishdf5;
2564 
2565   PetscFunctionBegin;
2566   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2567   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2568   if (!sectiondm) sectiondm = dm;
2569   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2570   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2571   if (globalDofSF) PetscAssertPointer(globalDofSF, 5);
2572   if (localDofSF) PetscAssertPointer(localDofSF, 6);
2573   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2574   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2575   if (ishdf5) {
2576 #if defined(PETSC_HAVE_HDF5)
2577     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2578 #else
2579     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2580 #endif
2581   }
2582   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2583   PetscFunctionReturn(PETSC_SUCCESS);
2584 }
2585 
2586 /*@
2587   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2588 
2589   Collective
2590 
2591   Input Parameters:
2592 + dm        - The `DM` that represents the topology
2593 . viewer    - The `PetscViewer` that represents the on-disk vector data
2594 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2595 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2596 - vec       - The global vector to set values of
2597 
2598   Level: advanced
2599 
2600   Notes:
2601   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 (or in case `sectiondm` is `NULL`) 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.
2602 
2603   Calling sequence:
2604 .vb
2605        DMCreate(PETSC_COMM_WORLD, &dm);
2606        DMSetType(dm, DMPLEX);
2607        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2608        DMPlexTopologyLoad(dm, viewer, &sfX);
2609        DMClone(dm, &sectiondm);
2610        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2611        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2612        DMGetGlobalVector(sectiondm, &vec);
2613        PetscObjectSetName((PetscObject)vec, "vec_name");
2614        DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2615        DMRestoreGlobalVector(sectiondm, &vec);
2616        PetscSFDestroy(&gsf);
2617        PetscSFDestroy(&sfX);
2618        DMDestroy(&sectiondm);
2619        DMDestroy(&dm);
2620 .ve
2621 
2622 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2623           `PetscSF`, `PetscViewer`
2624 @*/
2625 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2626 {
2627   PetscBool ishdf5;
2628 
2629   PetscFunctionBegin;
2630   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2631   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2632   if (!sectiondm) sectiondm = dm;
2633   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2634   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2635   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2636   /* Check consistency */
2637   {
2638     PetscSection section;
2639     PetscBool    includesConstraints;
2640     PetscInt     m, m1;
2641 
2642     PetscCall(VecGetLocalSize(vec, &m1));
2643     PetscCall(DMGetGlobalSection(sectiondm, &section));
2644     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2645     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2646     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2647     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2648   }
2649   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2650   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2651   if (ishdf5) {
2652 #if defined(PETSC_HAVE_HDF5)
2653     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2654 #else
2655     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2656 #endif
2657   }
2658   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2659   PetscFunctionReturn(PETSC_SUCCESS);
2660 }
2661 
2662 /*@
2663   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2664 
2665   Collective
2666 
2667   Input Parameters:
2668 + dm        - The `DM` that represents the topology
2669 . viewer    - The `PetscViewer` that represents the on-disk vector data
2670 . sectiondm - The `DM` that contains the local section on which vec is defined, can be `NULL`
2671 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2672 - vec       - The local vector to set values of
2673 
2674   Level: advanced
2675 
2676   Notes:
2677   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 (or in case `sectiondm` is `NULL`) 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.
2678 
2679   Calling sequence:
2680 .vb
2681        DMCreate(PETSC_COMM_WORLD, &dm);
2682        DMSetType(dm, DMPLEX);
2683        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2684        DMPlexTopologyLoad(dm, viewer, &sfX);
2685        DMClone(dm, &sectiondm);
2686        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2687        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2688        DMGetLocalVector(sectiondm, &vec);
2689        PetscObjectSetName((PetscObject)vec, "vec_name");
2690        DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2691        DMRestoreLocalVector(sectiondm, &vec);
2692        PetscSFDestroy(&lsf);
2693        PetscSFDestroy(&sfX);
2694        DMDestroy(&sectiondm);
2695        DMDestroy(&dm);
2696 .ve
2697 
2698 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2699           `PetscSF`, `PetscViewer`
2700 @*/
2701 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2702 {
2703   PetscBool ishdf5;
2704 
2705   PetscFunctionBegin;
2706   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2707   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2708   if (!sectiondm) sectiondm = dm;
2709   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2710   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2711   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2712   /* Check consistency */
2713   {
2714     PetscSection section;
2715     PetscBool    includesConstraints;
2716     PetscInt     m, m1;
2717 
2718     PetscCall(VecGetLocalSize(vec, &m1));
2719     PetscCall(DMGetLocalSection(sectiondm, &section));
2720     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2721     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2722     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2723     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2724   }
2725   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2726   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2727   if (ishdf5) {
2728 #if defined(PETSC_HAVE_HDF5)
2729     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2730 #else
2731     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2732 #endif
2733   }
2734   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2735   PetscFunctionReturn(PETSC_SUCCESS);
2736 }
2737 
2738 PetscErrorCode DMDestroy_Plex(DM dm)
2739 {
2740   DM_Plex *mesh = (DM_Plex *)dm->data;
2741 
2742   PetscFunctionBegin;
2743   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2744   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2745   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2746   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2747   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerivativeBoundaryValues_C", NULL));
2748   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2749   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2750   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2751   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2752   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2753   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2754   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetDefault_C", NULL));
2755   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetDefault_C", NULL));
2756   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetType_C", NULL));
2757   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetType_C", NULL));
2758   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2759   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2760   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetUseCeed_C", NULL));
2761   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetUseCeed_C", NULL));
2762   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", NULL));
2763   if (--mesh->refct > 0) PetscFunctionReturn(PETSC_SUCCESS);
2764   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2765   PetscCall(PetscFree(mesh->cones));
2766   PetscCall(PetscFree(mesh->coneOrientations));
2767   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2768   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2769   PetscCall(PetscFree(mesh->supports));
2770   PetscCall(PetscFree(mesh->cellTypes));
2771   PetscCall(DMPlexTransformDestroy(&mesh->tr));
2772   PetscCall(PetscFree(mesh->tetgenOpts));
2773   PetscCall(PetscFree(mesh->triangleOpts));
2774   PetscCall(PetscFree(mesh->transformType));
2775   PetscCall(PetscFree(mesh->distributionName));
2776   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2777   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2778   PetscCall(ISDestroy(&mesh->subpointIS));
2779   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2780   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2781   if (mesh->periodic.face_sfs) {
2782     for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(PetscSFDestroy(&mesh->periodic.face_sfs[i]));
2783     PetscCall(PetscFree(mesh->periodic.face_sfs));
2784   }
2785   PetscCall(PetscSFDestroy(&mesh->periodic.composed_sf));
2786   if (mesh->periodic.periodic_points) {
2787     for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(ISDestroy(&mesh->periodic.periodic_points[i]));
2788     PetscCall(PetscFree(mesh->periodic.periodic_points));
2789   }
2790   if (mesh->periodic.transform) PetscCall(PetscFree(mesh->periodic.transform));
2791   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2792   PetscCall(ISDestroy(&mesh->anchorIS));
2793   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2794   PetscCall(PetscFree(mesh->parents));
2795   PetscCall(PetscFree(mesh->childIDs));
2796   PetscCall(PetscSectionDestroy(&mesh->childSection));
2797   PetscCall(PetscFree(mesh->children));
2798   PetscCall(DMDestroy(&mesh->referenceTree));
2799   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2800   PetscCall(PetscFree(mesh->neighbors));
2801   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2802   if (mesh->nonempty_comm != MPI_COMM_NULL && mesh->nonempty_comm != MPI_COMM_SELF) PetscCallMPI(MPI_Comm_free(&mesh->nonempty_comm));
2803   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2804   PetscCall(PetscFree(mesh));
2805   PetscFunctionReturn(PETSC_SUCCESS);
2806 }
2807 
2808 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2809 {
2810   PetscSection           sectionGlobal, sectionLocal;
2811   PetscInt               bs = -1, mbs;
2812   PetscInt               localSize, localStart = 0;
2813   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2814   MatType                mtype;
2815   ISLocalToGlobalMapping ltog;
2816 
2817   PetscFunctionBegin;
2818   PetscCall(MatInitializePackage());
2819   mtype = dm->mattype;
2820   PetscCall(DMGetLocalSection(dm, &sectionLocal));
2821   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2822   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2823   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2824   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2825   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2826   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2827   PetscCall(MatSetType(*J, mtype));
2828   PetscCall(MatSetFromOptions(*J));
2829   PetscCall(MatGetBlockSize(*J, &mbs));
2830   if (mbs > 1) bs = mbs;
2831   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2832   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2833   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2834   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2835   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2836   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2837   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2838   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2839   if (!isShell) {
2840     // There are three states with pblocks, since block starts can have no dofs:
2841     // UNKNOWN) New Block:   An open block has been signalled by pblocks[p] == 1
2842     // TRUE)    Block Start: The first entry in a block has been added
2843     // FALSE)   Block Add:   An additional block entry has been added, since pblocks[p] == 0
2844     PetscBT         blst;
2845     PetscBool3      bstate     = PETSC_BOOL3_UNKNOWN;
2846     PetscBool       fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2847     const PetscInt *perm       = NULL;
2848     PetscInt       *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2849     PetscInt        pStart, pEnd, dof, cdof, num_fields;
2850 
2851     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2852     PetscCall(PetscSectionGetBlockStarts(sectionLocal, &blst));
2853     if (sectionLocal->perm) PetscCall(ISGetIndices(sectionLocal->perm, &perm));
2854 
2855     PetscCall(PetscCalloc1(localSize, &pblocks));
2856     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2857     PetscCall(PetscSectionGetNumFields(sectionGlobal, &num_fields));
2858     // We need to process in the permuted order to get block sizes right
2859     for (PetscInt point = pStart; point < pEnd; ++point) {
2860       const PetscInt p = perm ? perm[point] : point;
2861 
2862       switch (dm->blocking_type) {
2863       case DM_BLOCKING_TOPOLOGICAL_POINT: { // One block per topological point
2864         PetscInt bdof, offset;
2865 
2866         PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2867         PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2868         PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2869         if (blst && PetscBTLookup(blst, p)) bstate = PETSC_BOOL3_UNKNOWN;
2870         if (dof > 0) {
2871           // State change
2872           if (bstate == PETSC_BOOL3_UNKNOWN) bstate = PETSC_BOOL3_TRUE;
2873           else if (bstate == PETSC_BOOL3_TRUE && blst && !PetscBTLookup(blst, p)) bstate = PETSC_BOOL3_FALSE;
2874 
2875           for (PetscInt i = 0; i < dof - cdof; ++i) pblocks[offset - localStart + i] = dof - cdof;
2876           // Signal block concatenation
2877           if (bstate == PETSC_BOOL3_FALSE && dof - cdof) pblocks[offset - localStart] = -(dof - cdof);
2878         }
2879         dof  = dof < 0 ? -(dof + 1) : dof;
2880         bdof = cdof && (dof - cdof) ? 1 : dof;
2881         if (dof) {
2882           if (bs < 0) {
2883             bs = bdof;
2884           } else if (bs != bdof) {
2885             bs = 1;
2886           }
2887         }
2888       } break;
2889       case DM_BLOCKING_FIELD_NODE: {
2890         for (PetscInt field = 0; field < num_fields; field++) {
2891           PetscInt num_comp, bdof, offset;
2892           PetscCall(PetscSectionGetFieldComponents(sectionGlobal, field, &num_comp));
2893           PetscCall(PetscSectionGetFieldDof(sectionGlobal, p, field, &dof));
2894           if (dof < 0) continue;
2895           PetscCall(PetscSectionGetFieldOffset(sectionGlobal, p, field, &offset));
2896           PetscCall(PetscSectionGetFieldConstraintDof(sectionGlobal, p, field, &cdof));
2897           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);
2898           PetscInt num_nodes = dof / num_comp;
2899           for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = (dof - cdof) / num_nodes;
2900           // Handle possibly constant block size (unlikely)
2901           bdof = cdof && (dof - cdof) ? 1 : dof;
2902           if (dof) {
2903             if (bs < 0) {
2904               bs = bdof;
2905             } else if (bs != bdof) {
2906               bs = 1;
2907             }
2908           }
2909         }
2910       } break;
2911       }
2912     }
2913     if (sectionLocal->perm) PetscCall(ISRestoreIndices(sectionLocal->perm, &perm));
2914     /* Must have same blocksize on all procs (some might have no points) */
2915     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2916     bsLocal[1] = bs;
2917     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
2918     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2919     else bs = bsMinMax[0];
2920     bs = PetscMax(1, bs);
2921     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
2922     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2923       PetscCall(MatSetBlockSize(*J, bs));
2924       PetscCall(MatSetUp(*J));
2925     } else {
2926       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
2927       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2928       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2929     }
2930     if (pblocks) { // Consolidate blocks
2931       PetscInt nblocks = 0;
2932       pblocks[0]       = PetscAbs(pblocks[0]);
2933       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
2934         if (pblocks[i] == 0) continue;
2935         // Negative block size indicates the blocks should be concatenated
2936         if (pblocks[i] < 0) {
2937           pblocks[i] = -pblocks[i];
2938           pblocks[nblocks - 1] += pblocks[i];
2939         } else {
2940           pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2941         }
2942         for (PetscInt j = 1; j < pblocks[i]; j++)
2943           PetscCheck(pblocks[i + j] == pblocks[i], PETSC_COMM_SELF, PETSC_ERR_PLIB, "Block of size %" PetscInt_FMT " at %" PetscInt_FMT " mismatches entry %" PetscInt_FMT " at %" PetscInt_FMT, pblocks[i], i, pblocks[i + j], i + j);
2944       }
2945       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2946     }
2947     PetscCall(PetscFree(pblocks));
2948   }
2949   PetscCall(MatSetDM(*J, dm));
2950   PetscFunctionReturn(PETSC_SUCCESS);
2951 }
2952 
2953 /*@
2954   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2955 
2956   Not Collective
2957 
2958   Input Parameter:
2959 . dm - The `DMPLEX`
2960 
2961   Output Parameter:
2962 . subsection - The subdomain section
2963 
2964   Level: developer
2965 
2966 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscSection`
2967 @*/
2968 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2969 {
2970   DM_Plex *mesh = (DM_Plex *)dm->data;
2971 
2972   PetscFunctionBegin;
2973   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2974   if (!mesh->subdomainSection) {
2975     PetscSection section;
2976     PetscSF      sf;
2977 
2978     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
2979     PetscCall(DMGetLocalSection(dm, &section));
2980     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
2981     PetscCall(PetscSFDestroy(&sf));
2982   }
2983   *subsection = mesh->subdomainSection;
2984   PetscFunctionReturn(PETSC_SUCCESS);
2985 }
2986 
2987 /*@
2988   DMPlexGetChart - Return the interval for all mesh points [`pStart`, `pEnd`)
2989 
2990   Not Collective
2991 
2992   Input Parameter:
2993 . dm - The `DMPLEX`
2994 
2995   Output Parameters:
2996 + pStart - The first mesh point
2997 - pEnd   - The upper bound for mesh points
2998 
2999   Level: beginner
3000 
3001 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`
3002 @*/
3003 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
3004 {
3005   DM_Plex *mesh = (DM_Plex *)dm->data;
3006 
3007   PetscFunctionBegin;
3008   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3009   if (mesh->tr) PetscCall(DMPlexTransformGetChart(mesh->tr, pStart, pEnd));
3010   else PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
3011   PetscFunctionReturn(PETSC_SUCCESS);
3012 }
3013 
3014 /*@
3015   DMPlexSetChart - Set the interval for all mesh points [`pStart`, `pEnd`)
3016 
3017   Not Collective
3018 
3019   Input Parameters:
3020 + dm     - The `DMPLEX`
3021 . pStart - The first mesh point
3022 - pEnd   - The upper bound for mesh points
3023 
3024   Level: beginner
3025 
3026 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetChart()`
3027 @*/
3028 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
3029 {
3030   DM_Plex *mesh = (DM_Plex *)dm->data;
3031 
3032   PetscFunctionBegin;
3033   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3034   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
3035   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
3036   PetscCall(PetscFree(mesh->cellTypes));
3037   PetscFunctionReturn(PETSC_SUCCESS);
3038 }
3039 
3040 /*@
3041   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
3042 
3043   Not Collective
3044 
3045   Input Parameters:
3046 + dm - The `DMPLEX`
3047 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3048 
3049   Output Parameter:
3050 . size - The cone size for point `p`
3051 
3052   Level: beginner
3053 
3054 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3055 @*/
3056 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
3057 {
3058   DM_Plex *mesh = (DM_Plex *)dm->data;
3059 
3060   PetscFunctionBegin;
3061   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3062   PetscAssertPointer(size, 3);
3063   if (mesh->tr) PetscCall(DMPlexTransformGetConeSize(mesh->tr, p, size));
3064   else PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
3065   PetscFunctionReturn(PETSC_SUCCESS);
3066 }
3067 
3068 /*@
3069   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
3070 
3071   Not Collective
3072 
3073   Input Parameters:
3074 + dm   - The `DMPLEX`
3075 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3076 - size - The cone size for point `p`
3077 
3078   Level: beginner
3079 
3080   Note:
3081   This should be called after `DMPlexSetChart()`.
3082 
3083 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
3084 @*/
3085 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
3086 {
3087   DM_Plex *mesh = (DM_Plex *)dm->data;
3088 
3089   PetscFunctionBegin;
3090   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3091   PetscCheck(!mesh->tr, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Cannot call DMPlexSetConeSize() on a mesh with a transform defined.");
3092   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
3093   PetscFunctionReturn(PETSC_SUCCESS);
3094 }
3095 
3096 /*@C
3097   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
3098 
3099   Not Collective
3100 
3101   Input Parameters:
3102 + dm - The `DMPLEX`
3103 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3104 
3105   Output Parameter:
3106 . cone - An array of points which are on the in-edges for point `p`, the length of `cone` is the result of `DMPlexGetConeSize()`
3107 
3108   Level: beginner
3109 
3110   Fortran Notes:
3111   `cone` must be declared with
3112 .vb
3113   PetscInt, pointer :: cone(:)
3114 .ve
3115 
3116   You must also call `DMPlexRestoreCone()` after you finish using the array.
3117   `DMPlexRestoreCone()` is not needed/available in C.
3118 
3119 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`, `DMPlexRestoreCone()`
3120 @*/
3121 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
3122 {
3123   DM_Plex *mesh = (DM_Plex *)dm->data;
3124   PetscInt off;
3125 
3126   PetscFunctionBegin;
3127   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3128   PetscAssertPointer(cone, 3);
3129   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3130   *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3131   PetscFunctionReturn(PETSC_SUCCESS);
3132 }
3133 
3134 /*@
3135   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
3136 
3137   Not Collective
3138 
3139   Input Parameters:
3140 + dm - The `DMPLEX`
3141 - p  - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3142 
3143   Output Parameters:
3144 + pConesSection - `PetscSection` describing the layout of `pCones`
3145 - pCones        - An `IS` containing the points which are on the in-edges for the point set `p`
3146 
3147   Level: intermediate
3148 
3149 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`, `PetscSection`, `IS`
3150 @*/
3151 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
3152 {
3153   PetscSection cs, newcs;
3154   PetscInt    *cones;
3155   PetscInt    *newarr = NULL;
3156   PetscInt     n;
3157 
3158   PetscFunctionBegin;
3159   PetscCall(DMPlexGetCones(dm, &cones));
3160   PetscCall(DMPlexGetConeSection(dm, &cs));
3161   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
3162   if (pConesSection) *pConesSection = newcs;
3163   if (pCones) {
3164     PetscCall(PetscSectionGetStorageSize(newcs, &n));
3165     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
3166   }
3167   PetscFunctionReturn(PETSC_SUCCESS);
3168 }
3169 
3170 /*@
3171   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
3172 
3173   Not Collective
3174 
3175   Input Parameters:
3176 + dm     - The `DMPLEX`
3177 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3178 
3179   Output Parameter:
3180 . expandedPoints - An `IS` containing the of vertices recursively expanded from input points
3181 
3182   Level: advanced
3183 
3184   Notes:
3185   Like `DMPlexGetConeRecursive()` but returns only the 0-depth `IS` (i.e. vertices only) and no sections.
3186 
3187   There is no corresponding Restore function, just call `ISDestroy()` on the returned `IS` to deallocate.
3188 
3189 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`,
3190           `DMPlexGetDepth()`, `IS`
3191 @*/
3192 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
3193 {
3194   IS      *expandedPointsAll;
3195   PetscInt depth;
3196 
3197   PetscFunctionBegin;
3198   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3199   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3200   PetscAssertPointer(expandedPoints, 3);
3201   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3202   *expandedPoints = expandedPointsAll[0];
3203   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
3204   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3205   PetscFunctionReturn(PETSC_SUCCESS);
3206 }
3207 
3208 /*@
3209   DMPlexGetConeRecursive - Expand each given point into its cone points and do that recursively until we end up just with vertices
3210   (DAG points of depth 0, i.e., without cones).
3211 
3212   Not Collective
3213 
3214   Input Parameters:
3215 + dm     - The `DMPLEX`
3216 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3217 
3218   Output Parameters:
3219 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3220 . expandedPoints - (optional) An array of index sets with recursively expanded cones
3221 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3222 
3223   Level: advanced
3224 
3225   Notes:
3226   Like `DMPlexGetConeTuple()` but recursive.
3227 
3228   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.
3229   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
3230 
3231   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\:
3232   (1) DAG points in `expandedPoints`[d+1] with `depth` d+1 to their cone points in `expandedPoints`[d];
3233   (2) DAG points in `expandedPoints`[d+1] with `depth` in [0,d] to the same points in `expandedPoints`[d].
3234 
3235 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3236           `DMPlexGetDepth()`, `PetscSection`, `IS`
3237 @*/
3238 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3239 {
3240   const PetscInt *arr0 = NULL, *cone = NULL;
3241   PetscInt       *arr = NULL, *newarr = NULL;
3242   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
3243   IS             *expandedPoints_;
3244   PetscSection   *sections_;
3245 
3246   PetscFunctionBegin;
3247   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3248   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3249   if (depth) PetscAssertPointer(depth, 3);
3250   if (expandedPoints) PetscAssertPointer(expandedPoints, 4);
3251   if (sections) PetscAssertPointer(sections, 5);
3252   PetscCall(ISGetLocalSize(points, &n));
3253   PetscCall(ISGetIndices(points, &arr0));
3254   PetscCall(DMPlexGetDepth(dm, &depth_));
3255   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
3256   PetscCall(PetscCalloc1(depth_, &sections_));
3257   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
3258   for (d = depth_ - 1; d >= 0; d--) {
3259     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
3260     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
3261     for (i = 0; i < n; i++) {
3262       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
3263       if (arr[i] >= start && arr[i] < end) {
3264         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
3265         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
3266       } else {
3267         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
3268       }
3269     }
3270     PetscCall(PetscSectionSetUp(sections_[d]));
3271     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
3272     PetscCall(PetscMalloc1(newn, &newarr));
3273     for (i = 0; i < n; i++) {
3274       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
3275       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
3276       if (cn > 1) {
3277         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
3278         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
3279       } else {
3280         newarr[co] = arr[i];
3281       }
3282     }
3283     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
3284     arr = newarr;
3285     n   = newn;
3286   }
3287   PetscCall(ISRestoreIndices(points, &arr0));
3288   *depth = depth_;
3289   if (expandedPoints) *expandedPoints = expandedPoints_;
3290   else {
3291     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
3292     PetscCall(PetscFree(expandedPoints_));
3293   }
3294   if (sections) *sections = sections_;
3295   else {
3296     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
3297     PetscCall(PetscFree(sections_));
3298   }
3299   PetscFunctionReturn(PETSC_SUCCESS);
3300 }
3301 
3302 /*@
3303   DMPlexRestoreConeRecursive - Deallocates arrays created by `DMPlexGetConeRecursive()`
3304 
3305   Not Collective
3306 
3307   Input Parameters:
3308 + dm     - The `DMPLEX`
3309 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3310 
3311   Output Parameters:
3312 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3313 . expandedPoints - (optional) An array of recursively expanded cones
3314 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3315 
3316   Level: advanced
3317 
3318   Note:
3319   See `DMPlexGetConeRecursive()`
3320 
3321 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3322           `DMPlexGetDepth()`, `IS`, `PetscSection`
3323 @*/
3324 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3325 {
3326   PetscInt d, depth_;
3327 
3328   PetscFunctionBegin;
3329   PetscCall(DMPlexGetDepth(dm, &depth_));
3330   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
3331   if (depth) *depth = 0;
3332   if (expandedPoints) {
3333     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
3334     PetscCall(PetscFree(*expandedPoints));
3335   }
3336   if (sections) {
3337     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
3338     PetscCall(PetscFree(*sections));
3339   }
3340   PetscFunctionReturn(PETSC_SUCCESS);
3341 }
3342 
3343 /*@
3344   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
3345 
3346   Not Collective
3347 
3348   Input Parameters:
3349 + dm   - The `DMPLEX`
3350 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3351 - cone - An array of points which are on the in-edges for point `p`, its length must have been previously provided with `DMPlexSetConeSize()`
3352 
3353   Level: beginner
3354 
3355   Note:
3356   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3357 
3358 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3359 @*/
3360 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3361 {
3362   DM_Plex *mesh = (DM_Plex *)dm->data;
3363   PetscInt dof, off, c;
3364 
3365   PetscFunctionBegin;
3366   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3367   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3368   if (dof) PetscAssertPointer(cone, 3);
3369   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3370   if (PetscDefined(USE_DEBUG)) {
3371     PetscInt pStart, pEnd;
3372     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3373     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);
3374     for (c = 0; c < dof; ++c) {
3375       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);
3376       mesh->cones[off + c] = cone[c];
3377     }
3378   } else {
3379     for (c = 0; c < dof; ++c) mesh->cones[off + c] = cone[c];
3380   }
3381   PetscFunctionReturn(PETSC_SUCCESS);
3382 }
3383 
3384 /*@C
3385   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3386 
3387   Not Collective
3388 
3389   Input Parameters:
3390 + dm - The `DMPLEX`
3391 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3392 
3393   Output Parameter:
3394 . coneOrientation - An array of orientations which are on the in-edges for point `p`. An orientation is an
3395                     integer giving the prescription for cone traversal. Its length is given by the result of `DMPlexSetConeSize()`
3396 
3397   Level: beginner
3398 
3399   Note:
3400   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3401   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3402   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3403   with the identity.
3404 
3405   Fortran Notes:
3406   You must call `DMPlexRestoreConeOrientation()` after you finish using the returned array.
3407   `DMPlexRestoreConeOrientation()` is not needed/available in C.
3408 
3409 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetConeSize()`, `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`,
3410           `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3411 @*/
3412 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3413 {
3414   DM_Plex *mesh = (DM_Plex *)dm->data;
3415   PetscInt off;
3416 
3417   PetscFunctionBegin;
3418   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3419   if (PetscDefined(USE_DEBUG)) {
3420     PetscInt dof;
3421     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3422     if (dof) PetscAssertPointer(coneOrientation, 3);
3423   }
3424   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3425 
3426   *coneOrientation = &mesh->coneOrientations[off];
3427   PetscFunctionReturn(PETSC_SUCCESS);
3428 }
3429 
3430 /*@
3431   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3432 
3433   Not Collective
3434 
3435   Input Parameters:
3436 + dm              - The `DMPLEX`
3437 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3438 - coneOrientation - An array of orientations. Its length is given by the result of `DMPlexSetConeSize()`
3439 
3440   Level: beginner
3441 
3442   Notes:
3443   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3444 
3445   The meaning of coneOrientation is detailed in `DMPlexGetConeOrientation()`.
3446 
3447 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3448 @*/
3449 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3450 {
3451   DM_Plex *mesh = (DM_Plex *)dm->data;
3452   PetscInt pStart, pEnd;
3453   PetscInt dof, off, c;
3454 
3455   PetscFunctionBegin;
3456   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3457   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3458   if (dof) PetscAssertPointer(coneOrientation, 3);
3459   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3460   if (PetscDefined(USE_DEBUG)) {
3461     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3462     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);
3463     for (c = 0; c < dof; ++c) {
3464       PetscInt cdof, o = coneOrientation[c];
3465 
3466       PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3467       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);
3468       mesh->coneOrientations[off + c] = o;
3469     }
3470   } else {
3471     for (c = 0; c < dof; ++c) mesh->coneOrientations[off + c] = coneOrientation[c];
3472   }
3473   PetscFunctionReturn(PETSC_SUCCESS);
3474 }
3475 
3476 /*@
3477   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3478 
3479   Not Collective
3480 
3481   Input Parameters:
3482 + dm        - The `DMPLEX`
3483 . p         - The point, which must lie in the chart set with `DMPlexSetChart()`
3484 . conePos   - The local index in the cone where the point should be put
3485 - conePoint - The mesh point to insert
3486 
3487   Level: beginner
3488 
3489 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3490 @*/
3491 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3492 {
3493   DM_Plex *mesh = (DM_Plex *)dm->data;
3494   PetscInt pStart, pEnd;
3495   PetscInt dof, off;
3496 
3497   PetscFunctionBegin;
3498   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3499   if (PetscDefined(USE_DEBUG)) {
3500     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3501     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);
3502     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);
3503     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3504     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);
3505   }
3506   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3507   mesh->cones[off + conePos] = conePoint;
3508   PetscFunctionReturn(PETSC_SUCCESS);
3509 }
3510 
3511 /*@
3512   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3513 
3514   Not Collective
3515 
3516   Input Parameters:
3517 + dm              - The `DMPLEX`
3518 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3519 . conePos         - The local index in the cone where the point should be put
3520 - coneOrientation - The point orientation to insert
3521 
3522   Level: beginner
3523 
3524   Note:
3525   The meaning of coneOrientation values is detailed in `DMPlexGetConeOrientation()`.
3526 
3527 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3528 @*/
3529 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3530 {
3531   DM_Plex *mesh = (DM_Plex *)dm->data;
3532   PetscInt pStart, pEnd;
3533   PetscInt dof, off;
3534 
3535   PetscFunctionBegin;
3536   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3537   if (PetscDefined(USE_DEBUG)) {
3538     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3539     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);
3540     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3541     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);
3542   }
3543   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3544   mesh->coneOrientations[off + conePos] = coneOrientation;
3545   PetscFunctionReturn(PETSC_SUCCESS);
3546 }
3547 
3548 /*@C
3549   DMPlexGetOrientedCone - Return the points and orientations on the in-edges for this point in the DAG
3550 
3551   Not collective
3552 
3553   Input Parameters:
3554 + dm - The DMPlex
3555 - p  - The point, which must lie in the chart set with DMPlexSetChart()
3556 
3557   Output Parameters:
3558 + cone - An array of points which are on the in-edges for point `p`
3559 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3560          integer giving the prescription for cone traversal.
3561 
3562   Level: beginner
3563 
3564   Notes:
3565   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3566   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3567   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3568   with the identity.
3569 
3570   You must also call `DMPlexRestoreOrientedCone()` after you finish using the returned array.
3571 
3572   Fortran Notes:
3573   `cone` and `ornt` must be declared with
3574 .vb
3575   PetscInt, pointer :: cone(:)
3576   PetscInt, pointer :: ornt(:)
3577 .ve
3578 
3579 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3580 @*/
3581 PetscErrorCode DMPlexGetOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3582 {
3583   DM_Plex *mesh = (DM_Plex *)dm->data;
3584 
3585   PetscFunctionBegin;
3586   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3587   if (mesh->tr) {
3588     PetscCall(DMPlexTransformGetCone(mesh->tr, p, cone, ornt));
3589   } else {
3590     PetscInt off;
3591     if (PetscDefined(USE_DEBUG)) {
3592       PetscInt dof;
3593       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3594       if (dof) {
3595         if (cone) PetscAssertPointer(cone, 3);
3596         if (ornt) PetscAssertPointer(ornt, 4);
3597       }
3598     }
3599     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3600     if (cone) *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3601     if (ornt) *ornt = PetscSafePointerPlusOffset(mesh->coneOrientations, off);
3602   }
3603   PetscFunctionReturn(PETSC_SUCCESS);
3604 }
3605 
3606 /*@C
3607   DMPlexRestoreOrientedCone - Restore the points and orientations on the in-edges for this point in the DAG obtained with `DMPlexGetOrientedCone()`
3608 
3609   Not Collective
3610 
3611   Input Parameters:
3612 + dm   - The DMPlex
3613 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3614 . cone - An array of points which are on the in-edges for point p
3615 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3616          integer giving the prescription for cone traversal.
3617 
3618   Level: beginner
3619 
3620 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3621 @*/
3622 PetscErrorCode DMPlexRestoreOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3623 {
3624   DM_Plex *mesh = (DM_Plex *)dm->data;
3625 
3626   PetscFunctionBegin;
3627   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3628   if (mesh->tr) PetscCall(DMPlexTransformRestoreCone(mesh->tr, p, cone, ornt));
3629   PetscFunctionReturn(PETSC_SUCCESS);
3630 }
3631 
3632 /*@
3633   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3634 
3635   Not Collective
3636 
3637   Input Parameters:
3638 + dm - The `DMPLEX`
3639 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3640 
3641   Output Parameter:
3642 . size - The support size for point `p`
3643 
3644   Level: beginner
3645 
3646 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3647 @*/
3648 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3649 {
3650   DM_Plex *mesh = (DM_Plex *)dm->data;
3651 
3652   PetscFunctionBegin;
3653   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3654   PetscAssertPointer(size, 3);
3655   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3656   PetscFunctionReturn(PETSC_SUCCESS);
3657 }
3658 
3659 /*@
3660   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3661 
3662   Not Collective
3663 
3664   Input Parameters:
3665 + dm   - The `DMPLEX`
3666 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3667 - size - The support size for point `p`
3668 
3669   Level: beginner
3670 
3671   Note:
3672   This should be called after `DMPlexSetChart()`.
3673 
3674 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3675 @*/
3676 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3677 {
3678   DM_Plex *mesh = (DM_Plex *)dm->data;
3679 
3680   PetscFunctionBegin;
3681   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3682   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3683   PetscFunctionReturn(PETSC_SUCCESS);
3684 }
3685 
3686 /*@C
3687   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3688 
3689   Not Collective
3690 
3691   Input Parameters:
3692 + dm - The `DMPLEX`
3693 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3694 
3695   Output Parameter:
3696 . support - An array of points which are on the out-edges for point `p`, its length is that obtained from `DMPlexGetSupportSize()`
3697 
3698   Level: beginner
3699 
3700   Fortran Notes:
3701   `support` must be declared with
3702 .vb
3703   PetscInt, pointer :: support(:)
3704 .ve
3705 
3706   You must also call `DMPlexRestoreSupport()` after you finish using the returned array.
3707   `DMPlexRestoreSupport()` is not needed/available in C.
3708 
3709 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3710 @*/
3711 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3712 {
3713   DM_Plex *mesh = (DM_Plex *)dm->data;
3714   PetscInt off;
3715 
3716   PetscFunctionBegin;
3717   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3718   PetscAssertPointer(support, 3);
3719   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3720   *support = PetscSafePointerPlusOffset(mesh->supports, off);
3721   PetscFunctionReturn(PETSC_SUCCESS);
3722 }
3723 
3724 /*@
3725   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3726 
3727   Not Collective
3728 
3729   Input Parameters:
3730 + dm      - The `DMPLEX`
3731 . p       - The point, which must lie in the chart set with `DMPlexSetChart()`
3732 - support - An array of points which are on the out-edges for point `p`, its length is that obtained from `DMPlexGetSupportSize()`
3733 
3734   Level: beginner
3735 
3736   Note:
3737   This should be called after all calls to `DMPlexSetSupportSize()` and `DMSetUp()`.
3738 
3739 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3740 @*/
3741 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3742 {
3743   DM_Plex *mesh = (DM_Plex *)dm->data;
3744   PetscInt pStart, pEnd;
3745   PetscInt dof, off, c;
3746 
3747   PetscFunctionBegin;
3748   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3749   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3750   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3751   if (dof) PetscAssertPointer(support, 3);
3752   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3753   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);
3754   for (c = 0; c < dof; ++c) {
3755     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);
3756     mesh->supports[off + c] = support[c];
3757   }
3758   PetscFunctionReturn(PETSC_SUCCESS);
3759 }
3760 
3761 /*@
3762   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3763 
3764   Not Collective
3765 
3766   Input Parameters:
3767 + dm           - The `DMPLEX`
3768 . p            - The point, which must lie in the chart set with `DMPlexSetChart()`
3769 . supportPos   - The local index in the cone where the point should be put
3770 - supportPoint - The mesh point to insert
3771 
3772   Level: beginner
3773 
3774 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3775 @*/
3776 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3777 {
3778   DM_Plex *mesh = (DM_Plex *)dm->data;
3779   PetscInt pStart, pEnd;
3780   PetscInt dof, off;
3781 
3782   PetscFunctionBegin;
3783   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3784   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3785   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3786   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3787   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);
3788   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);
3789   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);
3790   mesh->supports[off + supportPos] = supportPoint;
3791   PetscFunctionReturn(PETSC_SUCCESS);
3792 }
3793 
3794 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3795 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3796 {
3797   switch (ct) {
3798   case DM_POLYTOPE_SEGMENT:
3799     if (o == -1) return -2;
3800     break;
3801   case DM_POLYTOPE_TRIANGLE:
3802     if (o == -3) return -1;
3803     if (o == -2) return -3;
3804     if (o == -1) return -2;
3805     break;
3806   case DM_POLYTOPE_QUADRILATERAL:
3807     if (o == -4) return -2;
3808     if (o == -3) return -1;
3809     if (o == -2) return -4;
3810     if (o == -1) return -3;
3811     break;
3812   default:
3813     return o;
3814   }
3815   return o;
3816 }
3817 
3818 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3819 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3820 {
3821   switch (ct) {
3822   case DM_POLYTOPE_SEGMENT:
3823     if ((o == -2) || (o == 1)) return -1;
3824     if (o == -1) return 0;
3825     break;
3826   case DM_POLYTOPE_TRIANGLE:
3827     if (o == -3) return -2;
3828     if (o == -2) return -1;
3829     if (o == -1) return -3;
3830     break;
3831   case DM_POLYTOPE_QUADRILATERAL:
3832     if (o == -4) return -2;
3833     if (o == -3) return -1;
3834     if (o == -2) return -4;
3835     if (o == -1) return -3;
3836     break;
3837   default:
3838     return o;
3839   }
3840   return o;
3841 }
3842 
3843 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3844 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3845 {
3846   PetscInt pStart, pEnd, p;
3847 
3848   PetscFunctionBegin;
3849   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3850   for (p = pStart; p < pEnd; ++p) {
3851     const PetscInt *cone, *ornt;
3852     PetscInt        coneSize, c;
3853 
3854     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3855     PetscCall(DMPlexGetCone(dm, p, &cone));
3856     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3857     for (c = 0; c < coneSize; ++c) {
3858       DMPolytopeType ct;
3859       const PetscInt o = ornt[c];
3860 
3861       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3862       switch (ct) {
3863       case DM_POLYTOPE_SEGMENT:
3864         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3865         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3866         break;
3867       case DM_POLYTOPE_TRIANGLE:
3868         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3869         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3870         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3871         break;
3872       case DM_POLYTOPE_QUADRILATERAL:
3873         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3874         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3875         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3876         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3877         break;
3878       default:
3879         break;
3880       }
3881     }
3882   }
3883   PetscFunctionReturn(PETSC_SUCCESS);
3884 }
3885 
3886 static inline PetscErrorCode DMPlexGetTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3887 {
3888   DM_Plex *mesh = (DM_Plex *)dm->data;
3889 
3890   PetscFunctionBeginHot;
3891   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3892     if (useCone) {
3893       PetscCall(DMPlexGetConeSize(dm, p, size));
3894       PetscCall(DMPlexGetOrientedCone(dm, p, arr, ornt));
3895     } else {
3896       PetscCall(DMPlexGetSupportSize(dm, p, size));
3897       PetscCall(DMPlexGetSupport(dm, p, arr));
3898     }
3899   } else {
3900     if (useCone) {
3901       const PetscSection s   = mesh->coneSection;
3902       const PetscInt     ps  = p - s->pStart;
3903       const PetscInt     off = s->atlasOff[ps];
3904 
3905       *size = s->atlasDof[ps];
3906       *arr  = mesh->cones + off;
3907       *ornt = mesh->coneOrientations + off;
3908     } else {
3909       const PetscSection s   = mesh->supportSection;
3910       const PetscInt     ps  = p - s->pStart;
3911       const PetscInt     off = s->atlasOff[ps];
3912 
3913       *size = s->atlasDof[ps];
3914       *arr  = mesh->supports + off;
3915     }
3916   }
3917   PetscFunctionReturn(PETSC_SUCCESS);
3918 }
3919 
3920 static inline PetscErrorCode DMPlexRestoreTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3921 {
3922   DM_Plex *mesh = (DM_Plex *)dm->data;
3923 
3924   PetscFunctionBeginHot;
3925   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3926     if (useCone) PetscCall(DMPlexRestoreOrientedCone(dm, p, arr, ornt));
3927   }
3928   PetscFunctionReturn(PETSC_SUCCESS);
3929 }
3930 
3931 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3932 {
3933   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3934   PetscInt       *closure;
3935   const PetscInt *tmp = NULL, *tmpO = NULL;
3936   PetscInt        off = 0, tmpSize, t;
3937 
3938   PetscFunctionBeginHot;
3939   if (ornt) {
3940     PetscCall(DMPlexGetCellType(dm, p, &ct));
3941     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;
3942   }
3943   if (*points) {
3944     closure = *points;
3945   } else {
3946     PetscInt maxConeSize, maxSupportSize;
3947     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3948     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
3949   }
3950   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3951   if (ct == DM_POLYTOPE_UNKNOWN) {
3952     closure[off++] = p;
3953     closure[off++] = 0;
3954     for (t = 0; t < tmpSize; ++t) {
3955       closure[off++] = tmp[t];
3956       closure[off++] = tmpO ? tmpO[t] : 0;
3957     }
3958   } else {
3959     const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, ornt);
3960 
3961     /* We assume that cells with a valid type have faces with a valid type */
3962     closure[off++] = p;
3963     closure[off++] = ornt;
3964     for (t = 0; t < tmpSize; ++t) {
3965       DMPolytopeType ft;
3966 
3967       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3968       closure[off++] = tmp[arr[t]];
3969       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3970     }
3971   }
3972   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3973   if (numPoints) *numPoints = tmpSize + 1;
3974   if (points) *points = closure;
3975   PetscFunctionReturn(PETSC_SUCCESS);
3976 }
3977 
3978 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
3979 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3980 {
3981   const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, o);
3982   const PetscInt *cone, *ornt;
3983   PetscInt       *pts, *closure = NULL;
3984   DMPolytopeType  ft;
3985   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3986   PetscInt        dim, coneSize, c, d, clSize, cl;
3987 
3988   PetscFunctionBeginHot;
3989   PetscCall(DMGetDimension(dm, &dim));
3990   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3991   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3992   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
3993   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
3994   maxSize       = PetscMax(coneSeries, supportSeries);
3995   if (*points) {
3996     pts = *points;
3997   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
3998   c        = 0;
3999   pts[c++] = point;
4000   pts[c++] = o;
4001   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
4002   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
4003   for (cl = 0; cl < clSize * 2; cl += 2) {
4004     pts[c++] = closure[cl];
4005     pts[c++] = closure[cl + 1];
4006   }
4007   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
4008   for (cl = 0; cl < clSize * 2; cl += 2) {
4009     pts[c++] = closure[cl];
4010     pts[c++] = closure[cl + 1];
4011   }
4012   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
4013   for (d = 2; d < coneSize; ++d) {
4014     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
4015     pts[c++] = cone[arr[d * 2 + 0]];
4016     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
4017   }
4018   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
4019   if (dim >= 3) {
4020     for (d = 2; d < coneSize; ++d) {
4021       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
4022       const PetscInt *fcone, *fornt;
4023       PetscInt        fconeSize, fc, i;
4024 
4025       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
4026       const PetscInt *farr = DMPolytopeTypeGetArrangement(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
4027       PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
4028       for (fc = 0; fc < fconeSize; ++fc) {
4029         const PetscInt cp = fcone[farr[fc * 2 + 0]];
4030         const PetscInt co = farr[fc * 2 + 1];
4031 
4032         for (i = 0; i < c; i += 2)
4033           if (pts[i] == cp) break;
4034         if (i == c) {
4035           PetscCall(DMPlexGetCellType(dm, cp, &ft));
4036           pts[c++] = cp;
4037           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
4038         }
4039       }
4040       PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
4041     }
4042   }
4043   *numPoints = c / 2;
4044   *points    = pts;
4045   PetscFunctionReturn(PETSC_SUCCESS);
4046 }
4047 
4048 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4049 {
4050   DMPolytopeType ct;
4051   PetscInt      *closure, *fifo;
4052   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
4053   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
4054   PetscInt       depth, maxSize;
4055 
4056   PetscFunctionBeginHot;
4057   PetscCall(DMPlexGetDepth(dm, &depth));
4058   if (depth == 1) {
4059     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
4060     PetscFunctionReturn(PETSC_SUCCESS);
4061   }
4062   PetscCall(DMPlexGetCellType(dm, p, &ct));
4063   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;
4064   if (DMPolytopeTypeIsHybrid(ct) && ct != DM_POLYTOPE_POINT_PRISM_TENSOR) {
4065     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
4066     PetscFunctionReturn(PETSC_SUCCESS);
4067   }
4068   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
4069   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
4070   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
4071   maxSize       = PetscMax(coneSeries, supportSeries);
4072   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
4073   if (*points) {
4074     closure = *points;
4075   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
4076   closure[closureSize++] = p;
4077   closure[closureSize++] = ornt;
4078   fifo[fifoSize++]       = p;
4079   fifo[fifoSize++]       = ornt;
4080   fifo[fifoSize++]       = ct;
4081   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
4082   while (fifoSize - fifoStart) {
4083     const PetscInt       q    = fifo[fifoStart++];
4084     const PetscInt       o    = fifo[fifoStart++];
4085     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
4086     const PetscInt      *qarr = DMPolytopeTypeGetArrangement(qt, o);
4087     const PetscInt      *tmp, *tmpO = NULL;
4088     PetscInt             tmpSize, t;
4089 
4090     if (PetscDefined(USE_DEBUG)) {
4091       PetscInt nO = DMPolytopeTypeGetNumArrangements(qt) / 2;
4092       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);
4093     }
4094     PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4095     for (t = 0; t < tmpSize; ++t) {
4096       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
4097       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
4098       const PetscInt cp = tmp[ip];
4099       PetscCall(DMPlexGetCellType(dm, cp, &ct));
4100       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
4101       PetscInt       c;
4102 
4103       /* Check for duplicate */
4104       for (c = 0; c < closureSize; c += 2) {
4105         if (closure[c] == cp) break;
4106       }
4107       if (c == closureSize) {
4108         closure[closureSize++] = cp;
4109         closure[closureSize++] = co;
4110         fifo[fifoSize++]       = cp;
4111         fifo[fifoSize++]       = co;
4112         fifo[fifoSize++]       = ct;
4113       }
4114     }
4115     PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4116   }
4117   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
4118   if (numPoints) *numPoints = closureSize / 2;
4119   if (points) *points = closure;
4120   PetscFunctionReturn(PETSC_SUCCESS);
4121 }
4122 
4123 /*@C
4124   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
4125 
4126   Not Collective
4127 
4128   Input Parameters:
4129 + dm      - The `DMPLEX`
4130 . p       - The mesh point
4131 - useCone - `PETSC_TRUE` for the closure, otherwise return the star
4132 
4133   Input/Output Parameter:
4134 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
4135            if *points is `NULL` on input, internal storage will be returned, use `DMPlexRestoreTransitiveClosure()`,
4136            otherwise the provided array is used to hold the values
4137 
4138   Output Parameter:
4139 . numPoints - The number of points in the closure, so `points` is of size 2*`numPoints`
4140 
4141   Level: beginner
4142 
4143   Note:
4144   If using internal storage (points is `NULL` on input), each call overwrites the last output.
4145 
4146   Fortran Notes:
4147   `points` must be declared with
4148 .vb
4149   PetscInt, pointer :: points(:)
4150 .ve
4151   and is always allocated by the function.
4152 
4153   The `numPoints` argument is not present in the Fortran binding.
4154 
4155 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4156 @*/
4157 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4158 {
4159   PetscFunctionBeginHot;
4160   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4161   if (numPoints) PetscAssertPointer(numPoints, 4);
4162   if (points) PetscAssertPointer(points, 5);
4163   if (PetscDefined(USE_DEBUG)) {
4164     PetscInt pStart, pEnd;
4165     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4166     PetscCheck(p >= pStart && p < pEnd, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Point %" PetscInt_FMT " is not in [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
4167   }
4168   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
4169   PetscFunctionReturn(PETSC_SUCCESS);
4170 }
4171 
4172 /*@C
4173   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
4174 
4175   Not Collective
4176 
4177   Input Parameters:
4178 + dm        - The `DMPLEX`
4179 . p         - The mesh point
4180 . useCone   - `PETSC_TRUE` for the closure, otherwise return the star
4181 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
4182 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
4183 
4184   Level: beginner
4185 
4186   Note:
4187   If not using internal storage (points is not `NULL` on input), this call is unnecessary
4188 
4189 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4190 @*/
4191 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4192 {
4193   PetscFunctionBeginHot;
4194   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4195   if (numPoints) *numPoints = 0;
4196   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
4197   PetscFunctionReturn(PETSC_SUCCESS);
4198 }
4199 
4200 /*@
4201   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
4202 
4203   Not Collective
4204 
4205   Input Parameter:
4206 . dm - The `DMPLEX`
4207 
4208   Output Parameters:
4209 + maxConeSize    - The maximum number of in-edges
4210 - maxSupportSize - The maximum number of out-edges
4211 
4212   Level: beginner
4213 
4214 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
4215 @*/
4216 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
4217 {
4218   DM_Plex *mesh = (DM_Plex *)dm->data;
4219 
4220   PetscFunctionBegin;
4221   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4222   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
4223   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
4224   PetscFunctionReturn(PETSC_SUCCESS);
4225 }
4226 
4227 PetscErrorCode DMSetUp_Plex(DM dm)
4228 {
4229   DM_Plex *mesh = (DM_Plex *)dm->data;
4230   PetscInt size, maxSupportSize;
4231 
4232   PetscFunctionBegin;
4233   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4234   PetscCall(PetscSectionSetUp(mesh->coneSection));
4235   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
4236   PetscCall(PetscMalloc1(size, &mesh->cones));
4237   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
4238   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4239   if (maxSupportSize) {
4240     PetscCall(PetscSectionSetUp(mesh->supportSection));
4241     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
4242     PetscCall(PetscMalloc1(size, &mesh->supports));
4243   }
4244   PetscFunctionReturn(PETSC_SUCCESS);
4245 }
4246 
4247 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
4248 {
4249   PetscFunctionBegin;
4250   if (subdm) PetscCall(DMClone(dm, subdm));
4251   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, NULL, NULL, is, subdm));
4252   if (subdm) (*subdm)->useNatural = dm->useNatural;
4253   if (dm->useNatural && dm->sfMigration) {
4254     PetscSF sfNatural;
4255 
4256     (*subdm)->sfMigration = dm->sfMigration;
4257     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
4258     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
4259     (*subdm)->sfNatural = sfNatural;
4260   }
4261   PetscFunctionReturn(PETSC_SUCCESS);
4262 }
4263 
4264 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
4265 {
4266   PetscInt i = 0;
4267 
4268   PetscFunctionBegin;
4269   PetscCall(DMClone(dms[0], superdm));
4270   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
4271   (*superdm)->useNatural = PETSC_FALSE;
4272   for (i = 0; i < len; i++) {
4273     if (dms[i]->useNatural && dms[i]->sfMigration) {
4274       PetscSF sfNatural;
4275 
4276       (*superdm)->sfMigration = dms[i]->sfMigration;
4277       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
4278       (*superdm)->useNatural = PETSC_TRUE;
4279       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
4280       (*superdm)->sfNatural = sfNatural;
4281       break;
4282     }
4283   }
4284   PetscFunctionReturn(PETSC_SUCCESS);
4285 }
4286 
4287 /*@
4288   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
4289 
4290   Not Collective
4291 
4292   Input Parameter:
4293 . dm - The `DMPLEX`
4294 
4295   Level: beginner
4296 
4297   Note:
4298   This should be called after all calls to `DMPlexSetCone()`
4299 
4300 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
4301 @*/
4302 PetscErrorCode DMPlexSymmetrize(DM dm)
4303 {
4304   DM_Plex  *mesh = (DM_Plex *)dm->data;
4305   PetscInt *offsets;
4306   PetscInt  supportSize;
4307   PetscInt  pStart, pEnd, p;
4308 
4309   PetscFunctionBegin;
4310   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4311   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
4312   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
4313   /* Calculate support sizes */
4314   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4315   for (p = pStart; p < pEnd; ++p) {
4316     PetscInt dof, off, c;
4317 
4318     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4319     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4320     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
4321   }
4322   PetscCall(PetscSectionSetUp(mesh->supportSection));
4323   /* Calculate supports */
4324   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
4325   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
4326   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
4327   for (p = pStart; p < pEnd; ++p) {
4328     PetscInt dof, off, c;
4329 
4330     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4331     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4332     for (c = off; c < off + dof; ++c) {
4333       const PetscInt q = mesh->cones[c];
4334       PetscInt       offS;
4335 
4336       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
4337 
4338       mesh->supports[offS + offsets[q]] = p;
4339       ++offsets[q];
4340     }
4341   }
4342   PetscCall(PetscFree(offsets));
4343   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
4344   PetscFunctionReturn(PETSC_SUCCESS);
4345 }
4346 
4347 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
4348 {
4349   IS stratumIS;
4350 
4351   PetscFunctionBegin;
4352   if (pStart >= pEnd) PetscFunctionReturn(PETSC_SUCCESS);
4353   if (PetscDefined(USE_DEBUG)) {
4354     PetscInt  qStart, qEnd, numLevels, level;
4355     PetscBool overlap = PETSC_FALSE;
4356     PetscCall(DMLabelGetNumValues(label, &numLevels));
4357     for (level = 0; level < numLevels; level++) {
4358       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4359       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
4360         overlap = PETSC_TRUE;
4361         break;
4362       }
4363     }
4364     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);
4365   }
4366   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
4367   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
4368   PetscCall(ISDestroy(&stratumIS));
4369   PetscFunctionReturn(PETSC_SUCCESS);
4370 }
4371 
4372 static PetscErrorCode DMPlexStratify_CellType_Private(DM dm, DMLabel label)
4373 {
4374   PetscInt *pMin, *pMax;
4375   PetscInt  pStart, pEnd;
4376   PetscInt  dmin = PETSC_MAX_INT, dmax = PETSC_MIN_INT;
4377 
4378   PetscFunctionBegin;
4379   {
4380     DMLabel label2;
4381 
4382     PetscCall(DMPlexGetCellTypeLabel(dm, &label2));
4383     PetscCall(PetscObjectViewFromOptions((PetscObject)label2, NULL, "-ct_view"));
4384   }
4385   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4386   for (PetscInt p = pStart; p < pEnd; ++p) {
4387     DMPolytopeType ct;
4388 
4389     PetscCall(DMPlexGetCellType(dm, p, &ct));
4390     dmin = PetscMin(DMPolytopeTypeGetDim(ct), dmin);
4391     dmax = PetscMax(DMPolytopeTypeGetDim(ct), dmax);
4392   }
4393   PetscCall(PetscMalloc2(dmax + 1, &pMin, dmax + 1, &pMax));
4394   for (PetscInt d = dmin; d <= dmax; ++d) {
4395     pMin[d] = PETSC_MAX_INT;
4396     pMax[d] = PETSC_MIN_INT;
4397   }
4398   for (PetscInt p = pStart; p < pEnd; ++p) {
4399     DMPolytopeType ct;
4400     PetscInt       d;
4401 
4402     PetscCall(DMPlexGetCellType(dm, p, &ct));
4403     d       = DMPolytopeTypeGetDim(ct);
4404     pMin[d] = PetscMin(p, pMin[d]);
4405     pMax[d] = PetscMax(p, pMax[d]);
4406   }
4407   for (PetscInt d = dmin; d <= dmax; ++d) {
4408     if (pMin[d] > pMax[d]) continue;
4409     PetscCall(DMPlexCreateDepthStratum(dm, label, d, pMin[d], pMax[d] + 1));
4410   }
4411   PetscCall(PetscFree2(pMin, pMax));
4412   PetscFunctionReturn(PETSC_SUCCESS);
4413 }
4414 
4415 static PetscErrorCode DMPlexStratify_Topological_Private(DM dm, DMLabel label)
4416 {
4417   PetscInt pStart, pEnd;
4418   PetscInt numRoots = 0, numLeaves = 0;
4419 
4420   PetscFunctionBegin;
4421   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4422   {
4423     /* Initialize roots and count leaves */
4424     PetscInt sMin = PETSC_MAX_INT;
4425     PetscInt sMax = PETSC_MIN_INT;
4426     PetscInt coneSize, supportSize;
4427 
4428     for (PetscInt p = pStart; p < pEnd; ++p) {
4429       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4430       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4431       if (!coneSize && supportSize) {
4432         sMin = PetscMin(p, sMin);
4433         sMax = PetscMax(p, sMax);
4434         ++numRoots;
4435       } else if (!supportSize && coneSize) {
4436         ++numLeaves;
4437       } else if (!supportSize && !coneSize) {
4438         /* Isolated points */
4439         sMin = PetscMin(p, sMin);
4440         sMax = PetscMax(p, sMax);
4441       }
4442     }
4443     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4444   }
4445 
4446   if (numRoots + numLeaves == (pEnd - pStart)) {
4447     PetscInt sMin = PETSC_MAX_INT;
4448     PetscInt sMax = PETSC_MIN_INT;
4449     PetscInt coneSize, supportSize;
4450 
4451     for (PetscInt p = pStart; p < pEnd; ++p) {
4452       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4453       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4454       if (!supportSize && coneSize) {
4455         sMin = PetscMin(p, sMin);
4456         sMax = PetscMax(p, sMax);
4457       }
4458     }
4459     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4460   } else {
4461     PetscInt level = 0;
4462     PetscInt qStart, qEnd;
4463 
4464     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4465     while (qEnd > qStart) {
4466       PetscInt sMin = PETSC_MAX_INT;
4467       PetscInt sMax = PETSC_MIN_INT;
4468 
4469       for (PetscInt q = qStart; q < qEnd; ++q) {
4470         const PetscInt *support;
4471         PetscInt        supportSize;
4472 
4473         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4474         PetscCall(DMPlexGetSupport(dm, q, &support));
4475         for (PetscInt s = 0; s < supportSize; ++s) {
4476           sMin = PetscMin(support[s], sMin);
4477           sMax = PetscMax(support[s], sMax);
4478         }
4479       }
4480       PetscCall(DMLabelGetNumValues(label, &level));
4481       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4482       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4483     }
4484   }
4485   PetscFunctionReturn(PETSC_SUCCESS);
4486 }
4487 
4488 /*@
4489   DMPlexStratify - Computes the strata for all points in the `DMPLEX`
4490 
4491   Collective
4492 
4493   Input Parameter:
4494 . dm - The `DMPLEX`
4495 
4496   Level: beginner
4497 
4498   Notes:
4499   The strata group all points of the same grade, and this function calculates the strata. This
4500   grade can be seen as the height (or depth) of the point in the DAG.
4501 
4502   The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
4503   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram).
4504   Concretely, `DMPlexStratify()` creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
4505   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
4506   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through `DMPlexGetDepthLabel()` or `DMPlexGetDepthStratum()`, or
4507   manually via `DMGetLabel()`.  The height is defined implicitly by height = maxDimension - depth, and can be accessed
4508   via `DMPlexGetHeightStratum()`.  For example, cells have height 0 and faces have height 1.
4509 
4510   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
4511   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
4512   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
4513   to interpolate only that one (e0), so that
4514 .vb
4515   cone(c0) = {e0, v2}
4516   cone(e0) = {v0, v1}
4517 .ve
4518   If `DMPlexStratify()` is run on this mesh, it will give depths
4519 .vb
4520    depth 0 = {v0, v1, v2}
4521    depth 1 = {e0, c0}
4522 .ve
4523   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
4524 
4525   `DMPlexStratify()` should be called after all calls to `DMPlexSymmetrize()`
4526 
4527 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
4528 @*/
4529 PetscErrorCode DMPlexStratify(DM dm)
4530 {
4531   DM_Plex  *mesh = (DM_Plex *)dm->data;
4532   DMLabel   label;
4533   PetscBool flg = PETSC_FALSE;
4534 
4535   PetscFunctionBegin;
4536   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4537   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
4538 
4539   // Create depth label
4540   PetscCall(DMRemoveLabel(dm, "depth", NULL));
4541   PetscCall(DMCreateLabel(dm, "depth"));
4542   PetscCall(DMPlexGetDepthLabel(dm, &label));
4543 
4544   PetscCall(PetscOptionsGetBool(NULL, dm->hdr.prefix, "-dm_plex_stratify_celltype", &flg, NULL));
4545   if (flg) PetscCall(DMPlexStratify_CellType_Private(dm, label));
4546   else PetscCall(DMPlexStratify_Topological_Private(dm, label));
4547 
4548   { /* just in case there is an empty process */
4549     PetscInt numValues, maxValues = 0, v;
4550 
4551     PetscCall(DMLabelGetNumValues(label, &numValues));
4552     PetscCall(MPIU_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4553     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4554   }
4555   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4556   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4557   PetscFunctionReturn(PETSC_SUCCESS);
4558 }
4559 
4560 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4561 {
4562   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4563   PetscInt       dim, depth, pheight, coneSize;
4564 
4565   PetscFunctionBeginHot;
4566   PetscCall(DMGetDimension(dm, &dim));
4567   PetscCall(DMPlexGetDepth(dm, &depth));
4568   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4569   pheight = depth - pdepth;
4570   if (depth <= 1) {
4571     switch (pdepth) {
4572     case 0:
4573       ct = DM_POLYTOPE_POINT;
4574       break;
4575     case 1:
4576       switch (coneSize) {
4577       case 2:
4578         ct = DM_POLYTOPE_SEGMENT;
4579         break;
4580       case 3:
4581         ct = DM_POLYTOPE_TRIANGLE;
4582         break;
4583       case 4:
4584         switch (dim) {
4585         case 2:
4586           ct = DM_POLYTOPE_QUADRILATERAL;
4587           break;
4588         case 3:
4589           ct = DM_POLYTOPE_TETRAHEDRON;
4590           break;
4591         default:
4592           break;
4593         }
4594         break;
4595       case 5:
4596         ct = DM_POLYTOPE_PYRAMID;
4597         break;
4598       case 6:
4599         ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4600         break;
4601       case 8:
4602         ct = DM_POLYTOPE_HEXAHEDRON;
4603         break;
4604       default:
4605         break;
4606       }
4607     }
4608   } else {
4609     if (pdepth == 0) {
4610       ct = DM_POLYTOPE_POINT;
4611     } else if (pheight == 0) {
4612       switch (dim) {
4613       case 1:
4614         switch (coneSize) {
4615         case 2:
4616           ct = DM_POLYTOPE_SEGMENT;
4617           break;
4618         default:
4619           break;
4620         }
4621         break;
4622       case 2:
4623         switch (coneSize) {
4624         case 3:
4625           ct = DM_POLYTOPE_TRIANGLE;
4626           break;
4627         case 4:
4628           ct = DM_POLYTOPE_QUADRILATERAL;
4629           break;
4630         default:
4631           break;
4632         }
4633         break;
4634       case 3:
4635         switch (coneSize) {
4636         case 4:
4637           ct = DM_POLYTOPE_TETRAHEDRON;
4638           break;
4639         case 5: {
4640           const PetscInt *cone;
4641           PetscInt        faceConeSize;
4642 
4643           PetscCall(DMPlexGetCone(dm, p, &cone));
4644           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4645           switch (faceConeSize) {
4646           case 3:
4647             ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4648             break;
4649           case 4:
4650             ct = DM_POLYTOPE_PYRAMID;
4651             break;
4652           }
4653         } break;
4654         case 6:
4655           ct = DM_POLYTOPE_HEXAHEDRON;
4656           break;
4657         default:
4658           break;
4659         }
4660         break;
4661       default:
4662         break;
4663       }
4664     } else if (pheight > 0) {
4665       switch (coneSize) {
4666       case 2:
4667         ct = DM_POLYTOPE_SEGMENT;
4668         break;
4669       case 3:
4670         ct = DM_POLYTOPE_TRIANGLE;
4671         break;
4672       case 4:
4673         ct = DM_POLYTOPE_QUADRILATERAL;
4674         break;
4675       default:
4676         break;
4677       }
4678     }
4679   }
4680   *pt = ct;
4681   PetscFunctionReturn(PETSC_SUCCESS);
4682 }
4683 
4684 /*@
4685   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4686 
4687   Collective
4688 
4689   Input Parameter:
4690 . dm - The `DMPLEX`
4691 
4692   Level: developer
4693 
4694   Note:
4695   This function is normally called automatically when a cell type is requested. It creates an
4696   internal `DMLabel` named "celltype" which can be directly accessed using `DMGetLabel()`. A user may disable
4697   automatic creation by creating the label manually, using `DMCreateLabel`(dm, "celltype").
4698 
4699   `DMPlexComputeCellTypes()` should be called after all calls to `DMPlexSymmetrize()` and `DMPlexStratify()`
4700 
4701 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4702 @*/
4703 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4704 {
4705   DM_Plex *mesh;
4706   DMLabel  ctLabel;
4707   PetscInt pStart, pEnd, p;
4708 
4709   PetscFunctionBegin;
4710   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4711   mesh = (DM_Plex *)dm->data;
4712   PetscCall(DMCreateLabel(dm, "celltype"));
4713   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4714   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4715   PetscCall(PetscFree(mesh->cellTypes));
4716   PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
4717   for (p = pStart; p < pEnd; ++p) {
4718     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4719     PetscInt       pdepth;
4720 
4721     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4722     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4723     PetscCheck(ct != DM_POLYTOPE_UNKNOWN && ct != DM_POLYTOPE_UNKNOWN_CELL && ct != DM_POLYTOPE_UNKNOWN_FACE, PETSC_COMM_SELF, PETSC_ERR_SUP, "Point %" PetscInt_FMT " has invalid celltype (%s)", p, DMPolytopeTypes[ct]);
4724     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4725     mesh->cellTypes[p - pStart].value_as_uint8 = ct;
4726   }
4727   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4728   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4729   PetscFunctionReturn(PETSC_SUCCESS);
4730 }
4731 
4732 /*@C
4733   DMPlexGetJoin - Get an array for the join of the set of points
4734 
4735   Not Collective
4736 
4737   Input Parameters:
4738 + dm        - The `DMPLEX` object
4739 . numPoints - The number of input points for the join
4740 - points    - The input points
4741 
4742   Output Parameters:
4743 + numCoveredPoints - The number of points in the join
4744 - coveredPoints    - The points in the join
4745 
4746   Level: intermediate
4747 
4748   Note:
4749   Currently, this is restricted to a single level join
4750 
4751   Fortran Notes:
4752   `converedPoints` must be declared with
4753 .vb
4754   PetscInt, pointer :: coveredPints(:)
4755 .ve
4756 
4757   The `numCoveredPoints` argument is not present in the Fortran binding.
4758 
4759 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4760 @*/
4761 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4762 {
4763   DM_Plex  *mesh = (DM_Plex *)dm->data;
4764   PetscInt *join[2];
4765   PetscInt  joinSize, i = 0;
4766   PetscInt  dof, off, p, c, m;
4767   PetscInt  maxSupportSize;
4768 
4769   PetscFunctionBegin;
4770   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4771   PetscAssertPointer(points, 3);
4772   PetscAssertPointer(numCoveredPoints, 4);
4773   PetscAssertPointer(coveredPoints, 5);
4774   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4775   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4776   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4777   /* Copy in support of first point */
4778   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4779   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4780   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4781   /* Check each successive support */
4782   for (p = 1; p < numPoints; ++p) {
4783     PetscInt newJoinSize = 0;
4784 
4785     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4786     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4787     for (c = 0; c < dof; ++c) {
4788       const PetscInt point = mesh->supports[off + c];
4789 
4790       for (m = 0; m < joinSize; ++m) {
4791         if (point == join[i][m]) {
4792           join[1 - i][newJoinSize++] = point;
4793           break;
4794         }
4795       }
4796     }
4797     joinSize = newJoinSize;
4798     i        = 1 - i;
4799   }
4800   *numCoveredPoints = joinSize;
4801   *coveredPoints    = join[i];
4802   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4803   PetscFunctionReturn(PETSC_SUCCESS);
4804 }
4805 
4806 /*@C
4807   DMPlexRestoreJoin - Restore an array for the join of the set of points obtained with `DMPlexGetJoin()`
4808 
4809   Not Collective
4810 
4811   Input Parameters:
4812 + dm        - The `DMPLEX` object
4813 . numPoints - The number of input points for the join
4814 - points    - The input points
4815 
4816   Output Parameters:
4817 + numCoveredPoints - The number of points in the join
4818 - coveredPoints    - The points in the join
4819 
4820   Level: intermediate
4821 
4822   Fortran Notes:
4823   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4824 
4825 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4826 @*/
4827 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4828 {
4829   PetscFunctionBegin;
4830   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4831   if (points) PetscAssertPointer(points, 3);
4832   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4833   PetscAssertPointer(coveredPoints, 5);
4834   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4835   if (numCoveredPoints) *numCoveredPoints = 0;
4836   PetscFunctionReturn(PETSC_SUCCESS);
4837 }
4838 
4839 /*@C
4840   DMPlexGetFullJoin - Get an array for the join of the set of points
4841 
4842   Not Collective
4843 
4844   Input Parameters:
4845 + dm        - The `DMPLEX` object
4846 . numPoints - The number of input points for the join
4847 - points    - The input points, its length is `numPoints`
4848 
4849   Output Parameters:
4850 + numCoveredPoints - The number of points in the join
4851 - coveredPoints    - The points in the join, its length is `numCoveredPoints`
4852 
4853   Level: intermediate
4854 
4855   Fortran Notes:
4856   `points` and `converedPoints` must be declared with
4857 .vb
4858   PetscInt, pointer :: points(:)
4859   PetscInt, pointer :: coveredPints(:)
4860 .ve
4861 
4862   The `numCoveredPoints` argument is not present in the Fortran binding.
4863 
4864 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4865 @*/
4866 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4867 {
4868   PetscInt *offsets, **closures;
4869   PetscInt *join[2];
4870   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4871   PetscInt  p, d, c, m, ms;
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, &depth));
4880   PetscCall(PetscCalloc1(numPoints, &closures));
4881   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4882   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4883   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4884   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4885   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4886 
4887   for (p = 0; p < numPoints; ++p) {
4888     PetscInt closureSize;
4889 
4890     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4891 
4892     offsets[p * (depth + 2) + 0] = 0;
4893     for (d = 0; d < depth + 1; ++d) {
4894       PetscInt pStart, pEnd, i;
4895 
4896       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4897       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4898         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4899           offsets[p * (depth + 2) + d + 1] = i;
4900           break;
4901         }
4902       }
4903       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4904     }
4905     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);
4906   }
4907   for (d = 0; d < depth + 1; ++d) {
4908     PetscInt dof;
4909 
4910     /* Copy in support of first point */
4911     dof = offsets[d + 1] - offsets[d];
4912     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4913     /* Check each successive cone */
4914     for (p = 1; p < numPoints && joinSize; ++p) {
4915       PetscInt newJoinSize = 0;
4916 
4917       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
4918       for (c = 0; c < dof; ++c) {
4919         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
4920 
4921         for (m = 0; m < joinSize; ++m) {
4922           if (point == join[i][m]) {
4923             join[1 - i][newJoinSize++] = point;
4924             break;
4925           }
4926         }
4927       }
4928       joinSize = newJoinSize;
4929       i        = 1 - i;
4930     }
4931     if (joinSize) break;
4932   }
4933   *numCoveredPoints = joinSize;
4934   *coveredPoints    = join[i];
4935   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4936   PetscCall(PetscFree(closures));
4937   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4938   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
4939   PetscFunctionReturn(PETSC_SUCCESS);
4940 }
4941 
4942 /*@C
4943   DMPlexGetMeet - Get an array for the meet of the set of points
4944 
4945   Not Collective
4946 
4947   Input Parameters:
4948 + dm        - The `DMPLEX` object
4949 . numPoints - The number of input points for the meet
4950 - points    - The input points, of length `numPoints`
4951 
4952   Output Parameters:
4953 + numCoveringPoints - The number of points in the meet
4954 - coveringPoints    - The points in the meet, of length `numCoveringPoints`
4955 
4956   Level: intermediate
4957 
4958   Note:
4959   Currently, this is restricted to a single level meet
4960 
4961   Fortran Notes:
4962   `coveringPoints` must be declared with
4963 .vb
4964   PetscInt, pointer :: coveringPoints(:)
4965 .ve
4966 
4967   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4968 
4969 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4970 @*/
4971 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt *coveringPoints[])
4972 {
4973   DM_Plex  *mesh = (DM_Plex *)dm->data;
4974   PetscInt *meet[2];
4975   PetscInt  meetSize, i = 0;
4976   PetscInt  dof, off, p, c, m;
4977   PetscInt  maxConeSize;
4978 
4979   PetscFunctionBegin;
4980   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4981   PetscAssertPointer(points, 3);
4982   PetscAssertPointer(numCoveringPoints, 4);
4983   PetscAssertPointer(coveringPoints, 5);
4984   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4985   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4986   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4987   /* Copy in cone of first point */
4988   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4989   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4990   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
4991   /* Check each successive cone */
4992   for (p = 1; p < numPoints; ++p) {
4993     PetscInt newMeetSize = 0;
4994 
4995     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4996     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4997     for (c = 0; c < dof; ++c) {
4998       const PetscInt point = mesh->cones[off + c];
4999 
5000       for (m = 0; m < meetSize; ++m) {
5001         if (point == meet[i][m]) {
5002           meet[1 - i][newMeetSize++] = point;
5003           break;
5004         }
5005       }
5006     }
5007     meetSize = newMeetSize;
5008     i        = 1 - i;
5009   }
5010   *numCoveringPoints = meetSize;
5011   *coveringPoints    = meet[i];
5012   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
5013   PetscFunctionReturn(PETSC_SUCCESS);
5014 }
5015 
5016 /*@C
5017   DMPlexRestoreMeet - Restore an array for the meet of the set of points obtained with `DMPlexGetMeet()`
5018 
5019   Not Collective
5020 
5021   Input Parameters:
5022 + dm        - The `DMPLEX` object
5023 . numPoints - The number of input points for the meet
5024 - points    - The input points
5025 
5026   Output Parameters:
5027 + numCoveredPoints - The number of points in the meet
5028 - coveredPoints    - The points in the meet
5029 
5030   Level: intermediate
5031 
5032   Fortran Notes:
5033   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
5034 
5035 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
5036 @*/
5037 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
5038 {
5039   PetscFunctionBegin;
5040   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5041   if (points) PetscAssertPointer(points, 3);
5042   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
5043   PetscAssertPointer(coveredPoints, 5);
5044   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
5045   if (numCoveredPoints) *numCoveredPoints = 0;
5046   PetscFunctionReturn(PETSC_SUCCESS);
5047 }
5048 
5049 /*@C
5050   DMPlexGetFullMeet - Get an array for the meet of the set of points
5051 
5052   Not Collective
5053 
5054   Input Parameters:
5055 + dm        - The `DMPLEX` object
5056 . numPoints - The number of input points for the meet
5057 - points    - The input points, of length  `numPoints`
5058 
5059   Output Parameters:
5060 + numCoveredPoints - The number of points in the meet
5061 - coveredPoints    - The points in the meet, of length  `numCoveredPoints`
5062 
5063   Level: intermediate
5064 
5065   Fortran Notes:
5066   `points` and `coveredPoints` must be declared with
5067 .vb
5068   PetscInt, pointer :: points(:)
5069   PetscInt, pointer :: coveredPoints(:)
5070 .ve
5071 
5072   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
5073 
5074 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
5075 @*/
5076 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
5077 {
5078   PetscInt *offsets, **closures;
5079   PetscInt *meet[2];
5080   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
5081   PetscInt  p, h, c, m, mc;
5082 
5083   PetscFunctionBegin;
5084   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5085   PetscAssertPointer(points, 3);
5086   PetscAssertPointer(numCoveredPoints, 4);
5087   PetscAssertPointer(coveredPoints, 5);
5088 
5089   PetscCall(DMPlexGetDepth(dm, &height));
5090   PetscCall(PetscMalloc1(numPoints, &closures));
5091   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5092   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
5093   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
5094   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
5095   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
5096 
5097   for (p = 0; p < numPoints; ++p) {
5098     PetscInt closureSize;
5099 
5100     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
5101 
5102     offsets[p * (height + 2) + 0] = 0;
5103     for (h = 0; h < height + 1; ++h) {
5104       PetscInt pStart, pEnd, i;
5105 
5106       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
5107       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
5108         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
5109           offsets[p * (height + 2) + h + 1] = i;
5110           break;
5111         }
5112       }
5113       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
5114     }
5115     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);
5116   }
5117   for (h = 0; h < height + 1; ++h) {
5118     PetscInt dof;
5119 
5120     /* Copy in cone of first point */
5121     dof = offsets[h + 1] - offsets[h];
5122     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
5123     /* Check each successive cone */
5124     for (p = 1; p < numPoints && meetSize; ++p) {
5125       PetscInt newMeetSize = 0;
5126 
5127       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
5128       for (c = 0; c < dof; ++c) {
5129         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
5130 
5131         for (m = 0; m < meetSize; ++m) {
5132           if (point == meet[i][m]) {
5133             meet[1 - i][newMeetSize++] = point;
5134             break;
5135           }
5136         }
5137       }
5138       meetSize = newMeetSize;
5139       i        = 1 - i;
5140     }
5141     if (meetSize) break;
5142   }
5143   *numCoveredPoints = meetSize;
5144   *coveredPoints    = meet[i];
5145   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
5146   PetscCall(PetscFree(closures));
5147   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5148   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
5149   PetscFunctionReturn(PETSC_SUCCESS);
5150 }
5151 
5152 /*@
5153   DMPlexEqual - Determine if two `DM` have the same topology
5154 
5155   Not Collective
5156 
5157   Input Parameters:
5158 + dmA - A `DMPLEX` object
5159 - dmB - A `DMPLEX` object
5160 
5161   Output Parameter:
5162 . equal - `PETSC_TRUE` if the topologies are identical
5163 
5164   Level: intermediate
5165 
5166   Note:
5167   We are not solving graph isomorphism, so we do not permute.
5168 
5169 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5170 @*/
5171 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
5172 {
5173   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
5174 
5175   PetscFunctionBegin;
5176   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
5177   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
5178   PetscAssertPointer(equal, 3);
5179 
5180   *equal = PETSC_FALSE;
5181   PetscCall(DMPlexGetDepth(dmA, &depth));
5182   PetscCall(DMPlexGetDepth(dmB, &depthB));
5183   if (depth != depthB) PetscFunctionReturn(PETSC_SUCCESS);
5184   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
5185   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
5186   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(PETSC_SUCCESS);
5187   for (p = pStart; p < pEnd; ++p) {
5188     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
5189     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
5190 
5191     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
5192     PetscCall(DMPlexGetCone(dmA, p, &cone));
5193     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
5194     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
5195     PetscCall(DMPlexGetCone(dmB, p, &coneB));
5196     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
5197     if (coneSize != coneSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5198     for (c = 0; c < coneSize; ++c) {
5199       if (cone[c] != coneB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5200       if (ornt[c] != orntB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5201     }
5202     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
5203     PetscCall(DMPlexGetSupport(dmA, p, &support));
5204     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
5205     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
5206     if (supportSize != supportSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5207     for (s = 0; s < supportSize; ++s) {
5208       if (support[s] != supportB[s]) PetscFunctionReturn(PETSC_SUCCESS);
5209     }
5210   }
5211   *equal = PETSC_TRUE;
5212   PetscFunctionReturn(PETSC_SUCCESS);
5213 }
5214 
5215 /*@
5216   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
5217 
5218   Not Collective
5219 
5220   Input Parameters:
5221 + dm         - The `DMPLEX`
5222 . cellDim    - The cell dimension
5223 - numCorners - The number of vertices on a cell
5224 
5225   Output Parameter:
5226 . numFaceVertices - The number of vertices on a face
5227 
5228   Level: developer
5229 
5230   Note:
5231   Of course this can only work for a restricted set of symmetric shapes
5232 
5233 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5234 @*/
5235 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
5236 {
5237   MPI_Comm comm;
5238 
5239   PetscFunctionBegin;
5240   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
5241   PetscAssertPointer(numFaceVertices, 4);
5242   switch (cellDim) {
5243   case 0:
5244     *numFaceVertices = 0;
5245     break;
5246   case 1:
5247     *numFaceVertices = 1;
5248     break;
5249   case 2:
5250     switch (numCorners) {
5251     case 3:                 /* triangle */
5252       *numFaceVertices = 2; /* Edge has 2 vertices */
5253       break;
5254     case 4:                 /* quadrilateral */
5255       *numFaceVertices = 2; /* Edge has 2 vertices */
5256       break;
5257     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
5258       *numFaceVertices = 3; /* Edge has 3 vertices */
5259       break;
5260     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
5261       *numFaceVertices = 3; /* Edge has 3 vertices */
5262       break;
5263     default:
5264       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5265     }
5266     break;
5267   case 3:
5268     switch (numCorners) {
5269     case 4:                 /* tetradehdron */
5270       *numFaceVertices = 3; /* Face has 3 vertices */
5271       break;
5272     case 6:                 /* tet cohesive cells */
5273       *numFaceVertices = 4; /* Face has 4 vertices */
5274       break;
5275     case 8:                 /* hexahedron */
5276       *numFaceVertices = 4; /* Face has 4 vertices */
5277       break;
5278     case 9:                 /* tet cohesive Lagrange cells */
5279       *numFaceVertices = 6; /* Face has 6 vertices */
5280       break;
5281     case 10:                /* quadratic tetrahedron */
5282       *numFaceVertices = 6; /* Face has 6 vertices */
5283       break;
5284     case 12:                /* hex cohesive Lagrange cells */
5285       *numFaceVertices = 6; /* Face has 6 vertices */
5286       break;
5287     case 18:                /* quadratic tet cohesive Lagrange cells */
5288       *numFaceVertices = 6; /* Face has 6 vertices */
5289       break;
5290     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
5291       *numFaceVertices = 9; /* Face has 9 vertices */
5292       break;
5293     default:
5294       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5295     }
5296     break;
5297   default:
5298     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
5299   }
5300   PetscFunctionReturn(PETSC_SUCCESS);
5301 }
5302 
5303 /*@
5304   DMPlexGetDepthLabel - Get the `DMLabel` recording the depth of each point
5305 
5306   Not Collective
5307 
5308   Input Parameter:
5309 . dm - The `DMPLEX` object
5310 
5311   Output Parameter:
5312 . depthLabel - The `DMLabel` recording point depth
5313 
5314   Level: developer
5315 
5316 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
5317 @*/
5318 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
5319 {
5320   PetscFunctionBegin;
5321   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5322   PetscAssertPointer(depthLabel, 2);
5323   *depthLabel = dm->depthLabel;
5324   PetscFunctionReturn(PETSC_SUCCESS);
5325 }
5326 
5327 /*@
5328   DMPlexGetDepth - Get the depth of the DAG representing this mesh
5329 
5330   Not Collective
5331 
5332   Input Parameter:
5333 . dm - The `DMPLEX` object
5334 
5335   Output Parameter:
5336 . depth - The number of strata (breadth first levels) in the DAG
5337 
5338   Level: developer
5339 
5340   Notes:
5341   This returns maximum of point depths over all points, i.e. maximum value of the label returned by `DMPlexGetDepthLabel()`.
5342 
5343   The point depth is described more in detail in `DMPlexGetDepthStratum()`.
5344 
5345   An empty mesh gives -1.
5346 
5347 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
5348 @*/
5349 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
5350 {
5351   DM_Plex *mesh = (DM_Plex *)dm->data;
5352   DMLabel  label;
5353   PetscInt d = -1;
5354 
5355   PetscFunctionBegin;
5356   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5357   PetscAssertPointer(depth, 2);
5358   if (mesh->tr) {
5359     PetscCall(DMPlexTransformGetDepth(mesh->tr, depth));
5360   } else {
5361     PetscCall(DMPlexGetDepthLabel(dm, &label));
5362     // Allow missing depths
5363     if (label) PetscCall(DMLabelGetValueBounds(label, NULL, &d));
5364     *depth = d;
5365   }
5366   PetscFunctionReturn(PETSC_SUCCESS);
5367 }
5368 
5369 /*@
5370   DMPlexGetDepthStratum - Get the bounds [`start`, `end`) for all points at a certain depth.
5371 
5372   Not Collective
5373 
5374   Input Parameters:
5375 + dm    - The `DMPLEX` object
5376 - depth - The requested depth
5377 
5378   Output Parameters:
5379 + start - The first point at this `depth`
5380 - end   - One beyond the last point at this `depth`
5381 
5382   Level: developer
5383 
5384   Notes:
5385   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
5386   often "vertices".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then depth stratum 1 contains the next
5387   higher dimension, e.g., "edges".
5388 
5389 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetHeightStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
5390 @*/
5391 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
5392 {
5393   DM_Plex *mesh = (DM_Plex *)dm->data;
5394   DMLabel  label;
5395   PetscInt pStart, pEnd;
5396 
5397   PetscFunctionBegin;
5398   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5399   if (start) {
5400     PetscAssertPointer(start, 3);
5401     *start = 0;
5402   }
5403   if (end) {
5404     PetscAssertPointer(end, 4);
5405     *end = 0;
5406   }
5407   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5408   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5409   if (depth < 0) {
5410     if (start) *start = pStart;
5411     if (end) *end = pEnd;
5412     PetscFunctionReturn(PETSC_SUCCESS);
5413   }
5414   if (mesh->tr) {
5415     PetscCall(DMPlexTransformGetDepthStratum(mesh->tr, depth, start, end));
5416   } else {
5417     PetscCall(DMPlexGetDepthLabel(dm, &label));
5418     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5419     PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
5420   }
5421   PetscFunctionReturn(PETSC_SUCCESS);
5422 }
5423 
5424 /*@
5425   DMPlexGetHeightStratum - Get the bounds [`start`, `end`) for all points at a certain height.
5426 
5427   Not Collective
5428 
5429   Input Parameters:
5430 + dm     - The `DMPLEX` object
5431 - height - The requested height
5432 
5433   Output Parameters:
5434 + start - The first point at this `height`
5435 - end   - One beyond the last point at this `height`
5436 
5437   Level: developer
5438 
5439   Notes:
5440   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
5441   points, often called "cells" or "elements".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then height
5442   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
5443 
5444 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5445 @*/
5446 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
5447 {
5448   DMLabel  label;
5449   PetscInt depth, pStart, pEnd;
5450 
5451   PetscFunctionBegin;
5452   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5453   if (start) {
5454     PetscAssertPointer(start, 3);
5455     *start = 0;
5456   }
5457   if (end) {
5458     PetscAssertPointer(end, 4);
5459     *end = 0;
5460   }
5461   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5462   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5463   if (height < 0) {
5464     if (start) *start = pStart;
5465     if (end) *end = pEnd;
5466     PetscFunctionReturn(PETSC_SUCCESS);
5467   }
5468   PetscCall(DMPlexGetDepthLabel(dm, &label));
5469   if (label) PetscCall(DMLabelGetNumValues(label, &depth));
5470   else PetscCall(DMGetDimension(dm, &depth));
5471   PetscCheck(depth >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Depth not yet computed");
5472   PetscCall(DMPlexGetDepthStratum(dm, depth - 1 - height, start, end));
5473   PetscFunctionReturn(PETSC_SUCCESS);
5474 }
5475 
5476 /*@
5477   DMPlexGetPointDepth - Get the `depth` of a given point
5478 
5479   Not Collective
5480 
5481   Input Parameters:
5482 + dm    - The `DMPLEX` object
5483 - point - The point
5484 
5485   Output Parameter:
5486 . depth - The depth of the `point`
5487 
5488   Level: intermediate
5489 
5490 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5491 @*/
5492 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
5493 {
5494   PetscFunctionBegin;
5495   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5496   PetscAssertPointer(depth, 3);
5497   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
5498   PetscFunctionReturn(PETSC_SUCCESS);
5499 }
5500 
5501 /*@
5502   DMPlexGetPointHeight - Get the `height` of a given point
5503 
5504   Not Collective
5505 
5506   Input Parameters:
5507 + dm    - The `DMPLEX` object
5508 - point - The point
5509 
5510   Output Parameter:
5511 . height - The height of the `point`
5512 
5513   Level: intermediate
5514 
5515 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
5516 @*/
5517 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5518 {
5519   PetscInt n, pDepth;
5520 
5521   PetscFunctionBegin;
5522   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5523   PetscAssertPointer(height, 3);
5524   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5525   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5526   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5527   PetscFunctionReturn(PETSC_SUCCESS);
5528 }
5529 
5530 /*@
5531   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5532 
5533   Not Collective
5534 
5535   Input Parameter:
5536 . dm - The `DMPLEX` object
5537 
5538   Output Parameter:
5539 . celltypeLabel - The `DMLabel` recording cell polytope type
5540 
5541   Level: developer
5542 
5543   Note:
5544   This function will trigger automatica computation of cell types. This can be disabled by calling
5545   `DMCreateLabel`(dm, "celltype") beforehand.
5546 
5547 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5548 @*/
5549 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5550 {
5551   PetscFunctionBegin;
5552   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5553   PetscAssertPointer(celltypeLabel, 2);
5554   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5555   *celltypeLabel = dm->celltypeLabel;
5556   PetscFunctionReturn(PETSC_SUCCESS);
5557 }
5558 
5559 /*@
5560   DMPlexGetCellType - Get the polytope type of a given cell
5561 
5562   Not Collective
5563 
5564   Input Parameters:
5565 + dm   - The `DMPLEX` object
5566 - cell - The cell
5567 
5568   Output Parameter:
5569 . celltype - The polytope type of the cell
5570 
5571   Level: intermediate
5572 
5573 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeType`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5574 @*/
5575 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5576 {
5577   DM_Plex *mesh = (DM_Plex *)dm->data;
5578   DMLabel  label;
5579   PetscInt ct;
5580 
5581   PetscFunctionBegin;
5582   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5583   PetscAssertPointer(celltype, 3);
5584   if (mesh->tr) {
5585     PetscCall(DMPlexTransformGetCellType(mesh->tr, cell, celltype));
5586   } else {
5587     PetscInt pStart, pEnd;
5588 
5589     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, NULL));
5590     if (!mesh->cellTypes) { /* XXX remove? optimize? */
5591       PetscCall(PetscSectionGetChart(mesh->coneSection, NULL, &pEnd));
5592       PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5593       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5594       for (PetscInt p = pStart; p < pEnd; p++) {
5595         PetscCall(DMLabelGetValue(label, p, &ct));
5596         mesh->cellTypes[p - pStart].value_as_uint8 = (DMPolytopeType)ct;
5597       }
5598     }
5599     *celltype = (DMPolytopeType)mesh->cellTypes[cell - pStart].value_as_uint8;
5600     if (PetscDefined(USE_DEBUG)) {
5601       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5602       PetscCall(DMLabelGetValue(label, cell, &ct));
5603       PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5604       PetscCheck(ct == (PetscInt)*celltype, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid cellType for %" PetscInt_FMT ": %d != %" PetscInt_FMT, cell, (int)*celltype, ct);
5605     }
5606   }
5607   PetscFunctionReturn(PETSC_SUCCESS);
5608 }
5609 
5610 /*@
5611   DMPlexSetCellType - Set the polytope type of a given cell
5612 
5613   Not Collective
5614 
5615   Input Parameters:
5616 + dm       - The `DMPLEX` object
5617 . cell     - The cell
5618 - celltype - The polytope type of the cell
5619 
5620   Level: advanced
5621 
5622   Note:
5623   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5624   is executed. This function will override the computed type. However, if automatic classification will not succeed
5625   and a user wants to manually specify all types, the classification must be disabled by calling
5626   DMCreateLabel(dm, "celltype") before getting or setting any cell types.
5627 
5628 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5629 @*/
5630 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5631 {
5632   DM_Plex *mesh = (DM_Plex *)dm->data;
5633   DMLabel  label;
5634   PetscInt pStart, pEnd;
5635 
5636   PetscFunctionBegin;
5637   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5638   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
5639   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5640   PetscCall(DMLabelSetValue(label, cell, celltype));
5641   if (!mesh->cellTypes) PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5642   mesh->cellTypes[cell - pStart].value_as_uint8 = celltype;
5643   PetscFunctionReturn(PETSC_SUCCESS);
5644 }
5645 
5646 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5647 {
5648   PetscSection section;
5649   PetscInt     maxHeight;
5650   const char  *prefix;
5651 
5652   PetscFunctionBegin;
5653   PetscCall(DMClone(dm, cdm));
5654   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5655   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5656   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5657   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5658   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5659   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5660   PetscCall(DMSetLocalSection(*cdm, section));
5661   PetscCall(PetscSectionDestroy(&section));
5662 
5663   PetscCall(DMSetNumFields(*cdm, 1));
5664   PetscCall(DMCreateDS(*cdm));
5665   (*cdm)->cloneOpts = PETSC_TRUE;
5666   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5667   PetscFunctionReturn(PETSC_SUCCESS);
5668 }
5669 
5670 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5671 {
5672   Vec coordsLocal, cellCoordsLocal;
5673   DM  coordsDM, cellCoordsDM;
5674 
5675   PetscFunctionBegin;
5676   *field = NULL;
5677   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5678   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5679   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5680   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5681   if (coordsLocal && coordsDM) {
5682     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5683     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5684   }
5685   PetscFunctionReturn(PETSC_SUCCESS);
5686 }
5687 
5688 /*@
5689   DMPlexGetConeSection - Return a section which describes the layout of cone data
5690 
5691   Not Collective
5692 
5693   Input Parameter:
5694 . dm - The `DMPLEX` object
5695 
5696   Output Parameter:
5697 . section - The `PetscSection` object
5698 
5699   Level: developer
5700 
5701 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5702 @*/
5703 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5704 {
5705   DM_Plex *mesh = (DM_Plex *)dm->data;
5706 
5707   PetscFunctionBegin;
5708   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5709   if (section) *section = mesh->coneSection;
5710   PetscFunctionReturn(PETSC_SUCCESS);
5711 }
5712 
5713 /*@
5714   DMPlexGetSupportSection - Return a section which describes the layout of support data
5715 
5716   Not Collective
5717 
5718   Input Parameter:
5719 . dm - The `DMPLEX` object
5720 
5721   Output Parameter:
5722 . section - The `PetscSection` object
5723 
5724   Level: developer
5725 
5726 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5727 @*/
5728 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5729 {
5730   DM_Plex *mesh = (DM_Plex *)dm->data;
5731 
5732   PetscFunctionBegin;
5733   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5734   if (section) *section = mesh->supportSection;
5735   PetscFunctionReturn(PETSC_SUCCESS);
5736 }
5737 
5738 /*@C
5739   DMPlexGetCones - Return cone data
5740 
5741   Not Collective
5742 
5743   Input Parameter:
5744 . dm - The `DMPLEX` object
5745 
5746   Output Parameter:
5747 . cones - The cone for each point
5748 
5749   Level: developer
5750 
5751 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5752 @*/
5753 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5754 {
5755   DM_Plex *mesh = (DM_Plex *)dm->data;
5756 
5757   PetscFunctionBegin;
5758   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5759   if (cones) *cones = mesh->cones;
5760   PetscFunctionReturn(PETSC_SUCCESS);
5761 }
5762 
5763 /*@C
5764   DMPlexGetConeOrientations - Return cone orientation data
5765 
5766   Not Collective
5767 
5768   Input Parameter:
5769 . dm - The `DMPLEX` object
5770 
5771   Output Parameter:
5772 . coneOrientations - The array of cone orientations for all points
5773 
5774   Level: developer
5775 
5776   Notes:
5777   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points
5778   as returned by `DMPlexGetConeOrientation()`.
5779 
5780   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5781 
5782 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5783 @*/
5784 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5785 {
5786   DM_Plex *mesh = (DM_Plex *)dm->data;
5787 
5788   PetscFunctionBegin;
5789   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5790   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5791   PetscFunctionReturn(PETSC_SUCCESS);
5792 }
5793 
5794 /******************************** FEM Support **********************************/
5795 
5796 PetscErrorCode DMPlexGetAllCells_Internal(DM plex, IS *cellIS)
5797 {
5798   PetscInt depth;
5799 
5800   PetscFunctionBegin;
5801   PetscCall(DMPlexGetDepth(plex, &depth));
5802   PetscCall(DMGetStratumIS(plex, "dim", depth, cellIS));
5803   if (!*cellIS) PetscCall(DMGetStratumIS(plex, "depth", depth, cellIS));
5804   PetscFunctionReturn(PETSC_SUCCESS);
5805 }
5806 
5807 PetscErrorCode DMPlexGetAllFaces_Internal(DM plex, IS *faceIS)
5808 {
5809   PetscInt depth;
5810 
5811   PetscFunctionBegin;
5812   PetscCall(DMPlexGetDepth(plex, &depth));
5813   PetscCall(DMGetStratumIS(plex, "dim", depth - 1, faceIS));
5814   if (!*faceIS) PetscCall(DMGetStratumIS(plex, "depth", depth - 1, faceIS));
5815   PetscFunctionReturn(PETSC_SUCCESS);
5816 }
5817 
5818 /*
5819  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5820  representing a line in the section.
5821 */
5822 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(DM dm, PetscSection section, PetscInt field, PetscInt line, PetscInt *Nc, PetscInt *k, PetscBool *continuous, PetscBool *tensor)
5823 {
5824   PetscObject  obj;
5825   PetscClassId id;
5826   PetscFE      fe = NULL;
5827 
5828   PetscFunctionBeginHot;
5829   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5830   PetscCall(DMGetField(dm, field, NULL, &obj));
5831   PetscCall(PetscObjectGetClassId(obj, &id));
5832   if (id == PETSCFE_CLASSID) fe = (PetscFE)obj;
5833 
5834   if (!fe) {
5835     /* Assume the full interpolated mesh is in the chart; lines in particular */
5836     /* An order k SEM disc has k-1 dofs on an edge */
5837     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5838     *k = *k / *Nc + 1;
5839   } else {
5840     PetscInt       dual_space_size, dim;
5841     PetscDualSpace dsp;
5842 
5843     PetscCall(DMGetDimension(dm, &dim));
5844     PetscCall(PetscFEGetDualSpace(fe, &dsp));
5845     PetscCall(PetscDualSpaceGetDimension(dsp, &dual_space_size));
5846     *k = (PetscInt)PetscCeilReal(PetscPowReal(dual_space_size / *Nc, 1.0 / dim)) - 1;
5847     PetscCall(PetscDualSpaceLagrangeGetContinuity(dsp, continuous));
5848     PetscCall(PetscDualSpaceLagrangeGetTensor(dsp, tensor));
5849   }
5850   PetscFunctionReturn(PETSC_SUCCESS);
5851 }
5852 
5853 static PetscErrorCode GetFieldSize_Private(PetscInt dim, PetscInt k, PetscBool tensor, PetscInt *dof)
5854 {
5855   PetscFunctionBeginHot;
5856   if (tensor) {
5857     *dof = PetscPowInt(k + 1, dim);
5858   } else {
5859     switch (dim) {
5860     case 1:
5861       *dof = k + 1;
5862       break;
5863     case 2:
5864       *dof = ((k + 1) * (k + 2)) / 2;
5865       break;
5866     case 3:
5867       *dof = ((k + 1) * (k + 2) * (k + 3)) / 6;
5868       break;
5869     default:
5870       *dof = 0;
5871     }
5872   }
5873   PetscFunctionReturn(PETSC_SUCCESS);
5874 }
5875 
5876 /*@
5877   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5878   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5879   section provided (or the section of the `DM`).
5880 
5881   Input Parameters:
5882 + dm      - The `DM`
5883 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or `PETSC_DETERMINE`
5884 - section - The `PetscSection` to reorder, or `NULL` for the default section
5885 
5886   Example:
5887   A typical interpolated single-quad mesh might order points as
5888 .vb
5889   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5890 
5891   v4 -- e6 -- v3
5892   |           |
5893   e7    c0    e8
5894   |           |
5895   v1 -- e5 -- v2
5896 .ve
5897 
5898   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5899   dofs in the order of points, e.g.,
5900 .vb
5901     c0 -> [0,1,2,3]
5902     v1 -> [4]
5903     ...
5904     e5 -> [8, 9]
5905 .ve
5906 
5907   which corresponds to the dofs
5908 .vb
5909     6   10  11  7
5910     13  2   3   15
5911     12  0   1   14
5912     4   8   9   5
5913 .ve
5914 
5915   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5916 .vb
5917   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5918 .ve
5919 
5920   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5921 .vb
5922    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5923 .ve
5924 
5925   Level: developer
5926 
5927   Notes:
5928   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5929   degree of the basis.
5930 
5931   This is required to run with libCEED.
5932 
5933 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5934 @*/
5935 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5936 {
5937   DMLabel   label;
5938   PetscInt  dim, depth = -1, eStart = -1, Nf;
5939   PetscBool continuous = PETSC_TRUE, tensor = PETSC_TRUE;
5940 
5941   PetscFunctionBegin;
5942   PetscCall(DMGetDimension(dm, &dim));
5943   if (dim < 1) PetscFunctionReturn(PETSC_SUCCESS);
5944   if (point < 0) {
5945     PetscInt sStart, sEnd;
5946 
5947     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5948     point = sEnd - sStart ? sStart : point;
5949   }
5950   PetscCall(DMPlexGetDepthLabel(dm, &label));
5951   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5952   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5953   if (depth == 1) {
5954     eStart = point;
5955   } else if (depth == dim) {
5956     const PetscInt *cone;
5957 
5958     PetscCall(DMPlexGetCone(dm, point, &cone));
5959     if (dim == 2) eStart = cone[0];
5960     else if (dim == 3) {
5961       const PetscInt *cone2;
5962       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5963       eStart = cone2[0];
5964     } 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);
5965   } 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);
5966 
5967   PetscCall(PetscSectionGetNumFields(section, &Nf));
5968   for (PetscInt d = 1; d <= dim; d++) {
5969     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5970     PetscInt *perm;
5971 
5972     for (f = 0; f < Nf; ++f) {
5973       PetscInt dof;
5974 
5975       PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5976       PetscCheck(dim == 1 || tensor || !continuous, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Continuous field %" PetscInt_FMT " must have a tensor product discretization", f);
5977       if (!continuous && d < dim) continue;
5978       PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
5979       size += dof * Nc;
5980     }
5981     PetscCall(PetscMalloc1(size, &perm));
5982     for (f = 0; f < Nf; ++f) {
5983       switch (d) {
5984       case 1:
5985         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5986         if (!continuous && d < dim) continue;
5987         /*
5988          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5989          We want              [ vtx0; edge of length k-1; vtx1 ]
5990          */
5991         if (continuous) {
5992           for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
5993           for (i = 0; i < k - 1; i++)
5994             for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
5995           for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
5996           foffset = offset;
5997         } else {
5998           PetscInt dof;
5999 
6000           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6001           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6002           foffset = offset;
6003         }
6004         break;
6005       case 2:
6006         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
6007         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6008         if (!continuous && d < dim) continue;
6009         /* The SEM order is
6010 
6011          v_lb, {e_b}, v_rb,
6012          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
6013          v_lt, reverse {e_t}, v_rt
6014          */
6015         if (continuous) {
6016           const PetscInt of   = 0;
6017           const PetscInt oeb  = of + PetscSqr(k - 1);
6018           const PetscInt oer  = oeb + (k - 1);
6019           const PetscInt oet  = oer + (k - 1);
6020           const PetscInt oel  = oet + (k - 1);
6021           const PetscInt ovlb = oel + (k - 1);
6022           const PetscInt ovrb = ovlb + 1;
6023           const PetscInt ovrt = ovrb + 1;
6024           const PetscInt ovlt = ovrt + 1;
6025           PetscInt       o;
6026 
6027           /* bottom */
6028           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
6029           for (o = oeb; o < oer; ++o)
6030             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6031           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
6032           /* middle */
6033           for (i = 0; i < k - 1; ++i) {
6034             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
6035             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
6036               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6037             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
6038           }
6039           /* top */
6040           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
6041           for (o = oel - 1; o >= oet; --o)
6042             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6043           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
6044           foffset = offset;
6045         } else {
6046           PetscInt dof;
6047 
6048           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6049           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6050           foffset = offset;
6051         }
6052         break;
6053       case 3:
6054         /* The original hex closure is
6055 
6056          {c,
6057          f_b, f_t, f_f, f_b, f_r, f_l,
6058          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
6059          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
6060          */
6061         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6062         if (!continuous && d < dim) continue;
6063         /* The SEM order is
6064          Bottom Slice
6065          v_blf, {e^{(k-1)-n}_bf}, v_brf,
6066          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
6067          v_blb, {e_bb}, v_brb,
6068 
6069          Middle Slice (j)
6070          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
6071          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
6072          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
6073 
6074          Top Slice
6075          v_tlf, {e_tf}, v_trf,
6076          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
6077          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
6078          */
6079         if (continuous) {
6080           const PetscInt oc    = 0;
6081           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
6082           const PetscInt oft   = ofb + PetscSqr(k - 1);
6083           const PetscInt off   = oft + PetscSqr(k - 1);
6084           const PetscInt ofk   = off + PetscSqr(k - 1);
6085           const PetscInt ofr   = ofk + PetscSqr(k - 1);
6086           const PetscInt ofl   = ofr + PetscSqr(k - 1);
6087           const PetscInt oebl  = ofl + PetscSqr(k - 1);
6088           const PetscInt oebb  = oebl + (k - 1);
6089           const PetscInt oebr  = oebb + (k - 1);
6090           const PetscInt oebf  = oebr + (k - 1);
6091           const PetscInt oetf  = oebf + (k - 1);
6092           const PetscInt oetr  = oetf + (k - 1);
6093           const PetscInt oetb  = oetr + (k - 1);
6094           const PetscInt oetl  = oetb + (k - 1);
6095           const PetscInt oerf  = oetl + (k - 1);
6096           const PetscInt oelf  = oerf + (k - 1);
6097           const PetscInt oelb  = oelf + (k - 1);
6098           const PetscInt oerb  = oelb + (k - 1);
6099           const PetscInt ovblf = oerb + (k - 1);
6100           const PetscInt ovblb = ovblf + 1;
6101           const PetscInt ovbrb = ovblb + 1;
6102           const PetscInt ovbrf = ovbrb + 1;
6103           const PetscInt ovtlf = ovbrf + 1;
6104           const PetscInt ovtrf = ovtlf + 1;
6105           const PetscInt ovtrb = ovtrf + 1;
6106           const PetscInt ovtlb = ovtrb + 1;
6107           PetscInt       o, n;
6108 
6109           /* Bottom Slice */
6110           /*   bottom */
6111           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
6112           for (o = oetf - 1; o >= oebf; --o)
6113             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6114           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
6115           /*   middle */
6116           for (i = 0; i < k - 1; ++i) {
6117             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
6118             for (n = 0; n < k - 1; ++n) {
6119               o = ofb + n * (k - 1) + i;
6120               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6121             }
6122             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
6123           }
6124           /*   top */
6125           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
6126           for (o = oebb; o < oebr; ++o)
6127             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6128           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
6129 
6130           /* Middle Slice */
6131           for (j = 0; j < k - 1; ++j) {
6132             /*   bottom */
6133             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
6134             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
6135               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6136             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
6137             /*   middle */
6138             for (i = 0; i < k - 1; ++i) {
6139               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
6140               for (n = 0; n < k - 1; ++n)
6141                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
6142               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
6143             }
6144             /*   top */
6145             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
6146             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
6147               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6148             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
6149           }
6150 
6151           /* Top Slice */
6152           /*   bottom */
6153           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
6154           for (o = oetf; o < oetr; ++o)
6155             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6156           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
6157           /*   middle */
6158           for (i = 0; i < k - 1; ++i) {
6159             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
6160             for (n = 0; n < k - 1; ++n)
6161               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
6162             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
6163           }
6164           /*   top */
6165           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
6166           for (o = oetl - 1; o >= oetb; --o)
6167             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6168           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
6169 
6170           foffset = offset;
6171         } else {
6172           PetscInt dof;
6173 
6174           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6175           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6176           foffset = offset;
6177         }
6178         break;
6179       default:
6180         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
6181       }
6182     }
6183     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
6184     /* Check permutation */
6185     {
6186       PetscInt *check;
6187 
6188       PetscCall(PetscMalloc1(size, &check));
6189       for (i = 0; i < size; ++i) {
6190         check[i] = -1;
6191         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
6192       }
6193       for (i = 0; i < size; ++i) check[perm[i]] = i;
6194       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
6195       PetscCall(PetscFree(check));
6196     }
6197     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
6198     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
6199       PetscInt *loc_perm;
6200       PetscCall(PetscMalloc1(size * 2, &loc_perm));
6201       for (PetscInt i = 0; i < size; i++) {
6202         loc_perm[i]        = perm[i];
6203         loc_perm[size + i] = size + perm[i];
6204       }
6205       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
6206     }
6207   }
6208   PetscFunctionReturn(PETSC_SUCCESS);
6209 }
6210 
6211 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
6212 {
6213   PetscDS  prob;
6214   PetscInt depth, Nf, h;
6215   DMLabel  label;
6216 
6217   PetscFunctionBeginHot;
6218   PetscCall(DMGetDS(dm, &prob));
6219   Nf      = prob->Nf;
6220   label   = dm->depthLabel;
6221   *dspace = NULL;
6222   if (field < Nf) {
6223     PetscObject disc = prob->disc[field];
6224 
6225     if (disc->classid == PETSCFE_CLASSID) {
6226       PetscDualSpace dsp;
6227 
6228       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
6229       PetscCall(DMLabelGetNumValues(label, &depth));
6230       PetscCall(DMLabelGetValue(label, point, &h));
6231       h = depth - 1 - h;
6232       if (h) {
6233         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
6234       } else {
6235         *dspace = dsp;
6236       }
6237     }
6238   }
6239   PetscFunctionReturn(PETSC_SUCCESS);
6240 }
6241 
6242 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6243 {
6244   PetscScalar       *array;
6245   const PetscScalar *vArray;
6246   const PetscInt    *cone, *coneO;
6247   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
6248 
6249   PetscFunctionBeginHot;
6250   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6251   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6252   PetscCall(DMPlexGetCone(dm, point, &cone));
6253   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6254   if (!values || !*values) {
6255     if ((point >= pStart) && (point < pEnd)) {
6256       PetscInt dof;
6257 
6258       PetscCall(PetscSectionGetDof(section, point, &dof));
6259       size += dof;
6260     }
6261     for (p = 0; p < numPoints; ++p) {
6262       const PetscInt cp = cone[p];
6263       PetscInt       dof;
6264 
6265       if ((cp < pStart) || (cp >= pEnd)) continue;
6266       PetscCall(PetscSectionGetDof(section, cp, &dof));
6267       size += dof;
6268     }
6269     if (!values) {
6270       if (csize) *csize = size;
6271       PetscFunctionReturn(PETSC_SUCCESS);
6272     }
6273     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
6274   } else {
6275     array = *values;
6276   }
6277   size = 0;
6278   PetscCall(VecGetArrayRead(v, &vArray));
6279   if ((point >= pStart) && (point < pEnd)) {
6280     PetscInt           dof, off, d;
6281     const PetscScalar *varr;
6282 
6283     PetscCall(PetscSectionGetDof(section, point, &dof));
6284     PetscCall(PetscSectionGetOffset(section, point, &off));
6285     varr = PetscSafePointerPlusOffset(vArray, off);
6286     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6287     size += dof;
6288   }
6289   for (p = 0; p < numPoints; ++p) {
6290     const PetscInt     cp = cone[p];
6291     PetscInt           o  = coneO[p];
6292     PetscInt           dof, off, d;
6293     const PetscScalar *varr;
6294 
6295     if ((cp < pStart) || (cp >= pEnd)) continue;
6296     PetscCall(PetscSectionGetDof(section, cp, &dof));
6297     PetscCall(PetscSectionGetOffset(section, cp, &off));
6298     varr = PetscSafePointerPlusOffset(vArray, off);
6299     if (o >= 0) {
6300       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6301     } else {
6302       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
6303     }
6304     size += dof;
6305   }
6306   PetscCall(VecRestoreArrayRead(v, &vArray));
6307   if (!*values) {
6308     if (csize) *csize = size;
6309     *values = array;
6310   } else {
6311     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6312     *csize = size;
6313   }
6314   PetscFunctionReturn(PETSC_SUCCESS);
6315 }
6316 
6317 /* Compress out points not in the section */
6318 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
6319 {
6320   const PetscInt np = *numPoints;
6321   PetscInt       pStart, pEnd, p, q;
6322 
6323   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6324   for (p = 0, q = 0; p < np; ++p) {
6325     const PetscInt r = points[p * 2];
6326     if ((r >= pStart) && (r < pEnd)) {
6327       points[q * 2]     = r;
6328       points[q * 2 + 1] = points[p * 2 + 1];
6329       ++q;
6330     }
6331   }
6332   *numPoints = q;
6333   return PETSC_SUCCESS;
6334 }
6335 
6336 /* Compressed closure does not apply closure permutation */
6337 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt ornt, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6338 {
6339   const PetscInt *cla = NULL;
6340   PetscInt        np, *pts = NULL;
6341 
6342   PetscFunctionBeginHot;
6343   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
6344   if (!ornt && *clPoints) {
6345     PetscInt dof, off;
6346 
6347     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
6348     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
6349     PetscCall(ISGetIndices(*clPoints, &cla));
6350     np  = dof / 2;
6351     pts = PetscSafePointerPlusOffset((PetscInt *)cla, off);
6352   } else {
6353     PetscCall(DMPlexGetTransitiveClosure_Internal(dm, point, ornt, PETSC_TRUE, &np, &pts));
6354     PetscCall(CompressPoints_Private(section, &np, pts));
6355   }
6356   *numPoints = np;
6357   *points    = pts;
6358   *clp       = cla;
6359   PetscFunctionReturn(PETSC_SUCCESS);
6360 }
6361 
6362 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6363 {
6364   PetscFunctionBeginHot;
6365   if (!*clPoints) {
6366     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
6367   } else {
6368     PetscCall(ISRestoreIndices(*clPoints, clp));
6369   }
6370   *numPoints = 0;
6371   *points    = NULL;
6372   *clSec     = NULL;
6373   *clPoints  = NULL;
6374   *clp       = NULL;
6375   PetscFunctionReturn(PETSC_SUCCESS);
6376 }
6377 
6378 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
6379 {
6380   PetscInt            offset = 0, p;
6381   const PetscInt    **perms  = NULL;
6382   const PetscScalar **flips  = NULL;
6383 
6384   PetscFunctionBeginHot;
6385   *size = 0;
6386   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6387   for (p = 0; p < numPoints; p++) {
6388     const PetscInt     point = points[2 * p];
6389     const PetscInt    *perm  = perms ? perms[p] : NULL;
6390     const PetscScalar *flip  = flips ? flips[p] : NULL;
6391     PetscInt           dof, off, d;
6392     const PetscScalar *varr;
6393 
6394     PetscCall(PetscSectionGetDof(section, point, &dof));
6395     PetscCall(PetscSectionGetOffset(section, point, &off));
6396     varr = PetscSafePointerPlusOffset(vArray, off);
6397     if (clperm) {
6398       if (perm) {
6399         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
6400       } else {
6401         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
6402       }
6403       if (flip) {
6404         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
6405       }
6406     } else {
6407       if (perm) {
6408         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
6409       } else {
6410         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
6411       }
6412       if (flip) {
6413         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
6414       }
6415     }
6416     offset += dof;
6417   }
6418   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6419   *size = offset;
6420   PetscFunctionReturn(PETSC_SUCCESS);
6421 }
6422 
6423 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[])
6424 {
6425   PetscInt offset = 0, f;
6426 
6427   PetscFunctionBeginHot;
6428   *size = 0;
6429   for (f = 0; f < numFields; ++f) {
6430     PetscInt            p;
6431     const PetscInt    **perms = NULL;
6432     const PetscScalar **flips = NULL;
6433 
6434     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6435     for (p = 0; p < numPoints; p++) {
6436       const PetscInt     point = points[2 * p];
6437       PetscInt           fdof, foff, b;
6438       const PetscScalar *varr;
6439       const PetscInt    *perm = perms ? perms[p] : NULL;
6440       const PetscScalar *flip = flips ? flips[p] : NULL;
6441 
6442       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6443       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6444       varr = &vArray[foff];
6445       if (clperm) {
6446         if (perm) {
6447           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
6448         } else {
6449           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
6450         }
6451         if (flip) {
6452           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
6453         }
6454       } else {
6455         if (perm) {
6456           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
6457         } else {
6458           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
6459         }
6460         if (flip) {
6461           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
6462         }
6463       }
6464       offset += fdof;
6465     }
6466     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6467   }
6468   *size = offset;
6469   PetscFunctionReturn(PETSC_SUCCESS);
6470 }
6471 
6472 PetscErrorCode DMPlexVecGetOrientedClosure_Internal(DM dm, PetscSection section, PetscBool useClPerm, Vec v, PetscInt point, PetscInt ornt, PetscInt *csize, PetscScalar *values[])
6473 {
6474   PetscSection    clSection;
6475   IS              clPoints;
6476   PetscInt       *points = NULL;
6477   const PetscInt *clp, *perm = NULL;
6478   PetscInt        depth, numFields, numPoints, asize;
6479 
6480   PetscFunctionBeginHot;
6481   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6482   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6483   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6484   PetscValidHeaderSpecific(v, VEC_CLASSID, 4);
6485   PetscCall(DMPlexGetDepth(dm, &depth));
6486   PetscCall(PetscSectionGetNumFields(section, &numFields));
6487   if (depth == 1 && numFields < 2) {
6488     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6489     PetscFunctionReturn(PETSC_SUCCESS);
6490   }
6491   /* Get points */
6492   PetscCall(DMPlexGetCompressedClosure(dm, section, point, ornt, &numPoints, &points, &clSection, &clPoints, &clp));
6493   /* Get sizes */
6494   asize = 0;
6495   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6496     PetscInt dof;
6497     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6498     asize += dof;
6499   }
6500   if (values) {
6501     const PetscScalar *vArray;
6502     PetscInt           size;
6503 
6504     if (*values) {
6505       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);
6506     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6507     if (useClPerm) PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6508     PetscCall(VecGetArrayRead(v, &vArray));
6509     /* Get values */
6510     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6511     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6512     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6513     /* Cleanup array */
6514     PetscCall(VecRestoreArrayRead(v, &vArray));
6515   }
6516   if (csize) *csize = asize;
6517   /* Cleanup points */
6518   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6519   PetscFunctionReturn(PETSC_SUCCESS);
6520 }
6521 
6522 /*@C
6523   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
6524 
6525   Not collective
6526 
6527   Input Parameters:
6528 + dm      - The `DM`
6529 . section - The section describing the layout in `v`, or `NULL` to use the default section
6530 . v       - The local vector
6531 - point   - The point in the `DM`
6532 
6533   Input/Output Parameters:
6534 + csize  - The size of the input values array, or `NULL`; on output the number of values in the closure
6535 - values - An array to use for the values, or *values = `NULL` to have it allocated automatically;
6536            if the user provided `NULL`, it is a borrowed array and should not be freed, use  `DMPlexVecRestoreClosure()` to return it
6537 
6538   Level: intermediate
6539 
6540   Notes:
6541   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6542   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
6543   assembly function, and a user may already have allocated storage for this operation.
6544 
6545   A typical use could be
6546 .vb
6547    values = NULL;
6548    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6549    for (cl = 0; cl < clSize; ++cl) {
6550      <Compute on closure>
6551    }
6552    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
6553 .ve
6554   or
6555 .vb
6556    PetscMalloc1(clMaxSize, &values);
6557    for (p = pStart; p < pEnd; ++p) {
6558      clSize = clMaxSize;
6559      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6560      for (cl = 0; cl < clSize; ++cl) {
6561        <Compute on closure>
6562      }
6563    }
6564    PetscFree(values);
6565 .ve
6566 
6567   Fortran Notes:
6568   The `csize` argument is not present in the Fortran binding.
6569 
6570   `values` must be declared with
6571 .vb
6572   PetscScalar,dimension(:),pointer   :: values
6573 .ve
6574   and it will be allocated internally by PETSc to hold the values returned
6575 
6576 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6577 @*/
6578 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6579 {
6580   PetscFunctionBeginHot;
6581   PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, section, PETSC_TRUE, v, point, 0, csize, values));
6582   PetscFunctionReturn(PETSC_SUCCESS);
6583 }
6584 
6585 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6586 {
6587   DMLabel            depthLabel;
6588   PetscSection       clSection;
6589   IS                 clPoints;
6590   PetscScalar       *array;
6591   const PetscScalar *vArray;
6592   PetscInt          *points = NULL;
6593   const PetscInt    *clp, *perm = NULL;
6594   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6595 
6596   PetscFunctionBeginHot;
6597   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6598   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6599   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6600   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6601   PetscCall(DMPlexGetDepth(dm, &mdepth));
6602   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6603   PetscCall(PetscSectionGetNumFields(section, &numFields));
6604   if (mdepth == 1 && numFields < 2) {
6605     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6606     PetscFunctionReturn(PETSC_SUCCESS);
6607   }
6608   /* Get points */
6609   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6610   for (clsize = 0, p = 0; p < Np; p++) {
6611     PetscInt dof;
6612     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6613     clsize += dof;
6614   }
6615   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6616   /* Filter points */
6617   for (p = 0; p < numPoints * 2; p += 2) {
6618     PetscInt dep;
6619 
6620     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6621     if (dep != depth) continue;
6622     points[Np * 2 + 0] = points[p];
6623     points[Np * 2 + 1] = points[p + 1];
6624     ++Np;
6625   }
6626   /* Get array */
6627   if (!values || !*values) {
6628     PetscInt asize = 0, dof;
6629 
6630     for (p = 0; p < Np * 2; p += 2) {
6631       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6632       asize += dof;
6633     }
6634     if (!values) {
6635       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6636       if (csize) *csize = asize;
6637       PetscFunctionReturn(PETSC_SUCCESS);
6638     }
6639     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6640   } else {
6641     array = *values;
6642   }
6643   PetscCall(VecGetArrayRead(v, &vArray));
6644   /* Get values */
6645   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6646   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6647   /* Cleanup points */
6648   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6649   /* Cleanup array */
6650   PetscCall(VecRestoreArrayRead(v, &vArray));
6651   if (!*values) {
6652     if (csize) *csize = size;
6653     *values = array;
6654   } else {
6655     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6656     *csize = size;
6657   }
6658   PetscFunctionReturn(PETSC_SUCCESS);
6659 }
6660 
6661 /*@C
6662   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point' obtained with `DMPlexVecGetClosure()`
6663 
6664   Not collective
6665 
6666   Input Parameters:
6667 + dm      - The `DM`
6668 . section - The section describing the layout in `v`, or `NULL` to use the default section
6669 . v       - The local vector
6670 . point   - The point in the `DM`
6671 . csize   - The number of values in the closure, or `NULL`
6672 - values  - The array of values
6673 
6674   Level: intermediate
6675 
6676   Note:
6677   The array values are discarded and not copied back into `v`. In order to copy values back to `v`, use `DMPlexVecSetClosure()`
6678 
6679   Fortran Note:
6680   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6681 
6682 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6683 @*/
6684 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6685 {
6686   PetscInt size = 0;
6687 
6688   PetscFunctionBegin;
6689   /* Should work without recalculating size */
6690   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6691   *values = NULL;
6692   PetscFunctionReturn(PETSC_SUCCESS);
6693 }
6694 
6695 static inline void add(PetscScalar *x, PetscScalar y)
6696 {
6697   *x += y;
6698 }
6699 static inline void insert(PetscScalar *x, PetscScalar y)
6700 {
6701   *x = y;
6702 }
6703 
6704 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[])
6705 {
6706   PetscInt        cdof;  /* The number of constraints on this point */
6707   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6708   PetscScalar    *a;
6709   PetscInt        off, cind = 0, k;
6710 
6711   PetscFunctionBegin;
6712   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6713   PetscCall(PetscSectionGetOffset(section, point, &off));
6714   a = &array[off];
6715   if (!cdof || setBC) {
6716     if (clperm) {
6717       if (perm) {
6718         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6719       } else {
6720         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6721       }
6722     } else {
6723       if (perm) {
6724         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6725       } else {
6726         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6727       }
6728     }
6729   } else {
6730     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6731     if (clperm) {
6732       if (perm) {
6733         for (k = 0; k < dof; ++k) {
6734           if ((cind < cdof) && (k == cdofs[cind])) {
6735             ++cind;
6736             continue;
6737           }
6738           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6739         }
6740       } else {
6741         for (k = 0; k < dof; ++k) {
6742           if ((cind < cdof) && (k == cdofs[cind])) {
6743             ++cind;
6744             continue;
6745           }
6746           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6747         }
6748       }
6749     } else {
6750       if (perm) {
6751         for (k = 0; k < dof; ++k) {
6752           if ((cind < cdof) && (k == cdofs[cind])) {
6753             ++cind;
6754             continue;
6755           }
6756           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6757         }
6758       } else {
6759         for (k = 0; k < dof; ++k) {
6760           if ((cind < cdof) && (k == cdofs[cind])) {
6761             ++cind;
6762             continue;
6763           }
6764           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6765         }
6766       }
6767     }
6768   }
6769   PetscFunctionReturn(PETSC_SUCCESS);
6770 }
6771 
6772 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[])
6773 {
6774   PetscInt        cdof;  /* The number of constraints on this point */
6775   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6776   PetscScalar    *a;
6777   PetscInt        off, cind = 0, k;
6778 
6779   PetscFunctionBegin;
6780   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6781   PetscCall(PetscSectionGetOffset(section, point, &off));
6782   a = &array[off];
6783   if (cdof) {
6784     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6785     if (clperm) {
6786       if (perm) {
6787         for (k = 0; k < dof; ++k) {
6788           if ((cind < cdof) && (k == cdofs[cind])) {
6789             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6790             cind++;
6791           }
6792         }
6793       } else {
6794         for (k = 0; k < dof; ++k) {
6795           if ((cind < cdof) && (k == cdofs[cind])) {
6796             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6797             cind++;
6798           }
6799         }
6800       }
6801     } else {
6802       if (perm) {
6803         for (k = 0; k < dof; ++k) {
6804           if ((cind < cdof) && (k == cdofs[cind])) {
6805             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6806             cind++;
6807           }
6808         }
6809       } else {
6810         for (k = 0; k < dof; ++k) {
6811           if ((cind < cdof) && (k == cdofs[cind])) {
6812             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6813             cind++;
6814           }
6815         }
6816       }
6817     }
6818   }
6819   PetscFunctionReturn(PETSC_SUCCESS);
6820 }
6821 
6822 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[])
6823 {
6824   PetscScalar    *a;
6825   PetscInt        fdof, foff, fcdof, foffset = *offset;
6826   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6827   PetscInt        cind = 0, b;
6828 
6829   PetscFunctionBegin;
6830   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6831   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6832   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6833   a = &array[foff];
6834   if (!fcdof || setBC) {
6835     if (clperm) {
6836       if (perm) {
6837         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6838       } else {
6839         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6840       }
6841     } else {
6842       if (perm) {
6843         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6844       } else {
6845         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6846       }
6847     }
6848   } else {
6849     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6850     if (clperm) {
6851       if (perm) {
6852         for (b = 0; b < fdof; b++) {
6853           if ((cind < fcdof) && (b == fcdofs[cind])) {
6854             ++cind;
6855             continue;
6856           }
6857           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6858         }
6859       } else {
6860         for (b = 0; b < fdof; b++) {
6861           if ((cind < fcdof) && (b == fcdofs[cind])) {
6862             ++cind;
6863             continue;
6864           }
6865           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6866         }
6867       }
6868     } else {
6869       if (perm) {
6870         for (b = 0; b < fdof; b++) {
6871           if ((cind < fcdof) && (b == fcdofs[cind])) {
6872             ++cind;
6873             continue;
6874           }
6875           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6876         }
6877       } else {
6878         for (b = 0; b < fdof; b++) {
6879           if ((cind < fcdof) && (b == fcdofs[cind])) {
6880             ++cind;
6881             continue;
6882           }
6883           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6884         }
6885       }
6886     }
6887   }
6888   *offset += fdof;
6889   PetscFunctionReturn(PETSC_SUCCESS);
6890 }
6891 
6892 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[])
6893 {
6894   PetscScalar    *a;
6895   PetscInt        fdof, foff, fcdof, foffset = *offset;
6896   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6897   PetscInt        Nc, cind = 0, ncind = 0, b;
6898   PetscBool       ncSet, fcSet;
6899 
6900   PetscFunctionBegin;
6901   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6902   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6903   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6904   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6905   a = &array[foff];
6906   if (fcdof) {
6907     /* We just override fcdof and fcdofs with Ncc and comps */
6908     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6909     if (clperm) {
6910       if (perm) {
6911         if (comps) {
6912           for (b = 0; b < fdof; b++) {
6913             ncSet = fcSet = PETSC_FALSE;
6914             if (b % Nc == comps[ncind]) {
6915               ncind = (ncind + 1) % Ncc;
6916               ncSet = PETSC_TRUE;
6917             }
6918             if ((cind < fcdof) && (b == fcdofs[cind])) {
6919               ++cind;
6920               fcSet = PETSC_TRUE;
6921             }
6922             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6923           }
6924         } else {
6925           for (b = 0; b < fdof; b++) {
6926             if ((cind < fcdof) && (b == fcdofs[cind])) {
6927               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6928               ++cind;
6929             }
6930           }
6931         }
6932       } else {
6933         if (comps) {
6934           for (b = 0; b < fdof; b++) {
6935             ncSet = fcSet = PETSC_FALSE;
6936             if (b % Nc == comps[ncind]) {
6937               ncind = (ncind + 1) % Ncc;
6938               ncSet = PETSC_TRUE;
6939             }
6940             if ((cind < fcdof) && (b == fcdofs[cind])) {
6941               ++cind;
6942               fcSet = PETSC_TRUE;
6943             }
6944             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6945           }
6946         } else {
6947           for (b = 0; b < fdof; b++) {
6948             if ((cind < fcdof) && (b == fcdofs[cind])) {
6949               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6950               ++cind;
6951             }
6952           }
6953         }
6954       }
6955     } else {
6956       if (perm) {
6957         if (comps) {
6958           for (b = 0; b < fdof; b++) {
6959             ncSet = fcSet = PETSC_FALSE;
6960             if (b % Nc == comps[ncind]) {
6961               ncind = (ncind + 1) % Ncc;
6962               ncSet = PETSC_TRUE;
6963             }
6964             if ((cind < fcdof) && (b == fcdofs[cind])) {
6965               ++cind;
6966               fcSet = PETSC_TRUE;
6967             }
6968             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6969           }
6970         } else {
6971           for (b = 0; b < fdof; b++) {
6972             if ((cind < fcdof) && (b == fcdofs[cind])) {
6973               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6974               ++cind;
6975             }
6976           }
6977         }
6978       } else {
6979         if (comps) {
6980           for (b = 0; b < fdof; b++) {
6981             ncSet = fcSet = PETSC_FALSE;
6982             if (b % Nc == comps[ncind]) {
6983               ncind = (ncind + 1) % Ncc;
6984               ncSet = PETSC_TRUE;
6985             }
6986             if ((cind < fcdof) && (b == fcdofs[cind])) {
6987               ++cind;
6988               fcSet = PETSC_TRUE;
6989             }
6990             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6991           }
6992         } else {
6993           for (b = 0; b < fdof; b++) {
6994             if ((cind < fcdof) && (b == fcdofs[cind])) {
6995               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6996               ++cind;
6997             }
6998           }
6999         }
7000       }
7001     }
7002   }
7003   *offset += fdof;
7004   PetscFunctionReturn(PETSC_SUCCESS);
7005 }
7006 
7007 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
7008 {
7009   PetscScalar    *array;
7010   const PetscInt *cone, *coneO;
7011   PetscInt        pStart, pEnd, p, numPoints, off, dof;
7012 
7013   PetscFunctionBeginHot;
7014   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
7015   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
7016   PetscCall(DMPlexGetCone(dm, point, &cone));
7017   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
7018   PetscCall(VecGetArray(v, &array));
7019   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
7020     const PetscInt cp = !p ? point : cone[p - 1];
7021     const PetscInt o  = !p ? 0 : coneO[p - 1];
7022 
7023     if ((cp < pStart) || (cp >= pEnd)) {
7024       dof = 0;
7025       continue;
7026     }
7027     PetscCall(PetscSectionGetDof(section, cp, &dof));
7028     /* ADD_VALUES */
7029     {
7030       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7031       PetscScalar    *a;
7032       PetscInt        cdof, coff, cind = 0, k;
7033 
7034       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
7035       PetscCall(PetscSectionGetOffset(section, cp, &coff));
7036       a = &array[coff];
7037       if (!cdof) {
7038         if (o >= 0) {
7039           for (k = 0; k < dof; ++k) a[k] += values[off + k];
7040         } else {
7041           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
7042         }
7043       } else {
7044         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
7045         if (o >= 0) {
7046           for (k = 0; k < dof; ++k) {
7047             if ((cind < cdof) && (k == cdofs[cind])) {
7048               ++cind;
7049               continue;
7050             }
7051             a[k] += values[off + k];
7052           }
7053         } else {
7054           for (k = 0; k < dof; ++k) {
7055             if ((cind < cdof) && (k == cdofs[cind])) {
7056               ++cind;
7057               continue;
7058             }
7059             a[k] += values[off + dof - k - 1];
7060           }
7061         }
7062       }
7063     }
7064   }
7065   PetscCall(VecRestoreArray(v, &array));
7066   PetscFunctionReturn(PETSC_SUCCESS);
7067 }
7068 
7069 /*@C
7070   DMPlexVecSetClosure - Set an array of the values on the closure of `point`
7071 
7072   Not collective
7073 
7074   Input Parameters:
7075 + dm      - The `DM`
7076 . section - The section describing the layout in `v`, or `NULL` to use the default section
7077 . v       - The local vector
7078 . point   - The point in the `DM`
7079 . values  - The array of values
7080 - mode    - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
7081             where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
7082 
7083   Level: intermediate
7084 
7085   Note:
7086   Usually the input arrays were obtained with `DMPlexVecGetClosure()`
7087 
7088   Fortran Note:
7089   `values` must be declared with
7090 .vb
7091   PetscScalar,dimension(:),pointer   :: values
7092 .ve
7093 
7094 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
7095 @*/
7096 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
7097 {
7098   PetscSection    clSection;
7099   IS              clPoints;
7100   PetscScalar    *array;
7101   PetscInt       *points = NULL;
7102   const PetscInt *clp, *clperm = NULL;
7103   PetscInt        depth, numFields, numPoints, p, clsize;
7104 
7105   PetscFunctionBeginHot;
7106   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7107   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7108   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7109   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7110   PetscCall(DMPlexGetDepth(dm, &depth));
7111   PetscCall(PetscSectionGetNumFields(section, &numFields));
7112   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
7113     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
7114     PetscFunctionReturn(PETSC_SUCCESS);
7115   }
7116   /* Get points */
7117   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7118   for (clsize = 0, p = 0; p < numPoints; p++) {
7119     PetscInt dof;
7120     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7121     clsize += dof;
7122   }
7123   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7124   /* Get array */
7125   PetscCall(VecGetArray(v, &array));
7126   /* Get values */
7127   if (numFields > 0) {
7128     PetscInt offset = 0, f;
7129     for (f = 0; f < numFields; ++f) {
7130       const PetscInt    **perms = NULL;
7131       const PetscScalar **flips = NULL;
7132 
7133       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7134       switch (mode) {
7135       case INSERT_VALUES:
7136         for (p = 0; p < numPoints; p++) {
7137           const PetscInt     point = points[2 * p];
7138           const PetscInt    *perm  = perms ? perms[p] : NULL;
7139           const PetscScalar *flip  = flips ? flips[p] : NULL;
7140           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array));
7141         }
7142         break;
7143       case INSERT_ALL_VALUES:
7144         for (p = 0; p < numPoints; p++) {
7145           const PetscInt     point = points[2 * p];
7146           const PetscInt    *perm  = perms ? perms[p] : NULL;
7147           const PetscScalar *flip  = flips ? flips[p] : NULL;
7148           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array));
7149         }
7150         break;
7151       case INSERT_BC_VALUES:
7152         for (p = 0; p < numPoints; p++) {
7153           const PetscInt     point = points[2 * p];
7154           const PetscInt    *perm  = perms ? perms[p] : NULL;
7155           const PetscScalar *flip  = flips ? flips[p] : NULL;
7156           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array));
7157         }
7158         break;
7159       case ADD_VALUES:
7160         for (p = 0; p < numPoints; p++) {
7161           const PetscInt     point = points[2 * p];
7162           const PetscInt    *perm  = perms ? perms[p] : NULL;
7163           const PetscScalar *flip  = flips ? flips[p] : NULL;
7164           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array));
7165         }
7166         break;
7167       case ADD_ALL_VALUES:
7168         for (p = 0; p < numPoints; p++) {
7169           const PetscInt     point = points[2 * p];
7170           const PetscInt    *perm  = perms ? perms[p] : NULL;
7171           const PetscScalar *flip  = flips ? flips[p] : NULL;
7172           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array));
7173         }
7174         break;
7175       case ADD_BC_VALUES:
7176         for (p = 0; p < numPoints; p++) {
7177           const PetscInt     point = points[2 * p];
7178           const PetscInt    *perm  = perms ? perms[p] : NULL;
7179           const PetscScalar *flip  = flips ? flips[p] : NULL;
7180           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array));
7181         }
7182         break;
7183       default:
7184         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7185       }
7186       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7187     }
7188   } else {
7189     PetscInt            dof, off;
7190     const PetscInt    **perms = NULL;
7191     const PetscScalar **flips = NULL;
7192 
7193     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
7194     switch (mode) {
7195     case INSERT_VALUES:
7196       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7197         const PetscInt     point = points[2 * p];
7198         const PetscInt    *perm  = perms ? perms[p] : NULL;
7199         const PetscScalar *flip  = flips ? flips[p] : NULL;
7200         PetscCall(PetscSectionGetDof(section, point, &dof));
7201         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array));
7202       }
7203       break;
7204     case INSERT_ALL_VALUES:
7205       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7206         const PetscInt     point = points[2 * p];
7207         const PetscInt    *perm  = perms ? perms[p] : NULL;
7208         const PetscScalar *flip  = flips ? flips[p] : NULL;
7209         PetscCall(PetscSectionGetDof(section, point, &dof));
7210         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array));
7211       }
7212       break;
7213     case INSERT_BC_VALUES:
7214       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7215         const PetscInt     point = points[2 * p];
7216         const PetscInt    *perm  = perms ? perms[p] : NULL;
7217         const PetscScalar *flip  = flips ? flips[p] : NULL;
7218         PetscCall(PetscSectionGetDof(section, point, &dof));
7219         PetscCall(updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array));
7220       }
7221       break;
7222     case ADD_VALUES:
7223       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7224         const PetscInt     point = points[2 * p];
7225         const PetscInt    *perm  = perms ? perms[p] : NULL;
7226         const PetscScalar *flip  = flips ? flips[p] : NULL;
7227         PetscCall(PetscSectionGetDof(section, point, &dof));
7228         PetscCall(updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array));
7229       }
7230       break;
7231     case ADD_ALL_VALUES:
7232       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7233         const PetscInt     point = points[2 * p];
7234         const PetscInt    *perm  = perms ? perms[p] : NULL;
7235         const PetscScalar *flip  = flips ? flips[p] : NULL;
7236         PetscCall(PetscSectionGetDof(section, point, &dof));
7237         PetscCall(updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array));
7238       }
7239       break;
7240     case ADD_BC_VALUES:
7241       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7242         const PetscInt     point = points[2 * p];
7243         const PetscInt    *perm  = perms ? perms[p] : NULL;
7244         const PetscScalar *flip  = flips ? flips[p] : NULL;
7245         PetscCall(PetscSectionGetDof(section, point, &dof));
7246         PetscCall(updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array));
7247       }
7248       break;
7249     default:
7250       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7251     }
7252     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
7253   }
7254   /* Cleanup points */
7255   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7256   /* Cleanup array */
7257   PetscCall(VecRestoreArray(v, &array));
7258   PetscFunctionReturn(PETSC_SUCCESS);
7259 }
7260 
7261 /* Check whether the given point is in the label. If not, update the offset to skip this point */
7262 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
7263 {
7264   PetscFunctionBegin;
7265   *contains = PETSC_TRUE;
7266   if (label) {
7267     PetscInt fdof;
7268 
7269     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
7270     if (!*contains) {
7271       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7272       *offset += fdof;
7273       PetscFunctionReturn(PETSC_SUCCESS);
7274     }
7275   }
7276   PetscFunctionReturn(PETSC_SUCCESS);
7277 }
7278 
7279 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
7280 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)
7281 {
7282   PetscSection    clSection;
7283   IS              clPoints;
7284   PetscScalar    *array;
7285   PetscInt       *points = NULL;
7286   const PetscInt *clp;
7287   PetscInt        numFields, numPoints, p;
7288   PetscInt        offset = 0, f;
7289 
7290   PetscFunctionBeginHot;
7291   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7292   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7293   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7294   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7295   PetscCall(PetscSectionGetNumFields(section, &numFields));
7296   /* Get points */
7297   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7298   /* Get array */
7299   PetscCall(VecGetArray(v, &array));
7300   /* Get values */
7301   for (f = 0; f < numFields; ++f) {
7302     const PetscInt    **perms = NULL;
7303     const PetscScalar **flips = NULL;
7304     PetscBool           contains;
7305 
7306     if (!fieldActive[f]) {
7307       for (p = 0; p < numPoints * 2; p += 2) {
7308         PetscInt fdof;
7309         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7310         offset += fdof;
7311       }
7312       continue;
7313     }
7314     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7315     switch (mode) {
7316     case INSERT_VALUES:
7317       for (p = 0; p < numPoints; p++) {
7318         const PetscInt     point = points[2 * p];
7319         const PetscInt    *perm  = perms ? perms[p] : NULL;
7320         const PetscScalar *flip  = flips ? flips[p] : NULL;
7321         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7322         if (!contains) continue;
7323         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
7324       }
7325       break;
7326     case INSERT_ALL_VALUES:
7327       for (p = 0; p < numPoints; p++) {
7328         const PetscInt     point = points[2 * p];
7329         const PetscInt    *perm  = perms ? perms[p] : NULL;
7330         const PetscScalar *flip  = flips ? flips[p] : NULL;
7331         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7332         if (!contains) continue;
7333         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
7334       }
7335       break;
7336     case INSERT_BC_VALUES:
7337       for (p = 0; p < numPoints; p++) {
7338         const PetscInt     point = points[2 * p];
7339         const PetscInt    *perm  = perms ? perms[p] : NULL;
7340         const PetscScalar *flip  = flips ? flips[p] : NULL;
7341         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7342         if (!contains) continue;
7343         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
7344       }
7345       break;
7346     case ADD_VALUES:
7347       for (p = 0; p < numPoints; p++) {
7348         const PetscInt     point = points[2 * p];
7349         const PetscInt    *perm  = perms ? perms[p] : NULL;
7350         const PetscScalar *flip  = flips ? flips[p] : NULL;
7351         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7352         if (!contains) continue;
7353         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
7354       }
7355       break;
7356     case ADD_ALL_VALUES:
7357       for (p = 0; p < numPoints; p++) {
7358         const PetscInt     point = points[2 * p];
7359         const PetscInt    *perm  = perms ? perms[p] : NULL;
7360         const PetscScalar *flip  = flips ? flips[p] : NULL;
7361         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7362         if (!contains) continue;
7363         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
7364       }
7365       break;
7366     default:
7367       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7368     }
7369     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7370   }
7371   /* Cleanup points */
7372   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7373   /* Cleanup array */
7374   PetscCall(VecRestoreArray(v, &array));
7375   PetscFunctionReturn(PETSC_SUCCESS);
7376 }
7377 
7378 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
7379 {
7380   PetscMPIInt rank;
7381   PetscInt    i, j;
7382 
7383   PetscFunctionBegin;
7384   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7385   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
7386   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
7387   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
7388   numCIndices = numCIndices ? numCIndices : numRIndices;
7389   if (!values) PetscFunctionReturn(PETSC_SUCCESS);
7390   for (i = 0; i < numRIndices; i++) {
7391     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
7392     for (j = 0; j < numCIndices; j++) {
7393 #if defined(PETSC_USE_COMPLEX)
7394       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
7395 #else
7396       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
7397 #endif
7398     }
7399     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
7400   }
7401   PetscFunctionReturn(PETSC_SUCCESS);
7402 }
7403 
7404 /*
7405   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
7406 
7407   Input Parameters:
7408 + section - The section for this data layout
7409 . islocal - Is the section (and thus indices being requested) local or global?
7410 . point   - The point contributing dofs with these indices
7411 . off     - The global offset of this point
7412 . loff    - The local offset of each field
7413 . setBC   - The flag determining whether to include indices of boundary values
7414 . perm    - A permutation of the dofs on this point, or NULL
7415 - indperm - A permutation of the entire indices array, or NULL
7416 
7417   Output Parameter:
7418 . indices - Indices for dofs on this point
7419 
7420   Level: developer
7421 
7422   Note: The indices could be local or global, depending on the value of 'off'.
7423 */
7424 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
7425 {
7426   PetscInt        dof;   /* The number of unknowns on this point */
7427   PetscInt        cdof;  /* The number of constraints on this point */
7428   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7429   PetscInt        cind = 0, k;
7430 
7431   PetscFunctionBegin;
7432   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7433   PetscCall(PetscSectionGetDof(section, point, &dof));
7434   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
7435   if (!cdof || setBC) {
7436     for (k = 0; k < dof; ++k) {
7437       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7438       const PetscInt ind    = indperm ? indperm[preind] : preind;
7439 
7440       indices[ind] = off + k;
7441     }
7442   } else {
7443     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
7444     for (k = 0; k < dof; ++k) {
7445       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7446       const PetscInt ind    = indperm ? indperm[preind] : preind;
7447 
7448       if ((cind < cdof) && (k == cdofs[cind])) {
7449         /* Insert check for returning constrained indices */
7450         indices[ind] = -(off + k + 1);
7451         ++cind;
7452       } else {
7453         indices[ind] = off + k - (islocal ? 0 : cind);
7454       }
7455     }
7456   }
7457   *loff += dof;
7458   PetscFunctionReturn(PETSC_SUCCESS);
7459 }
7460 
7461 /*
7462  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
7463 
7464  Input Parameters:
7465 + section - a section (global or local)
7466 - islocal - `PETSC_TRUE` if requesting local indices (i.e., section is local); `PETSC_FALSE` for global
7467 . point - point within section
7468 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
7469 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
7470 . setBC - identify constrained (boundary condition) points via involution.
7471 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
7472 . permsoff - offset
7473 - indperm - index permutation
7474 
7475  Output Parameter:
7476 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
7477 . indices - array to hold indices (as defined by section) of each dof associated with point
7478 
7479  Notes:
7480  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
7481  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
7482  in the local vector.
7483 
7484  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
7485  significant).  It is invalid to call with a global section and setBC=true.
7486 
7487  Developer Note:
7488  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
7489  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
7490  offset could be obtained from the section instead of passing it explicitly as we do now.
7491 
7492  Example:
7493  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
7494  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
7495  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
7496  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.
7497 
7498  Level: developer
7499 */
7500 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[])
7501 {
7502   PetscInt numFields, foff, f;
7503 
7504   PetscFunctionBegin;
7505   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7506   PetscCall(PetscSectionGetNumFields(section, &numFields));
7507   for (f = 0, foff = 0; f < numFields; ++f) {
7508     PetscInt        fdof, cfdof;
7509     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7510     PetscInt        cind = 0, b;
7511     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7512 
7513     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7514     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7515     if (!cfdof || setBC) {
7516       for (b = 0; b < fdof; ++b) {
7517         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7518         const PetscInt ind    = indperm ? indperm[preind] : preind;
7519 
7520         indices[ind] = off + foff + b;
7521       }
7522     } else {
7523       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7524       for (b = 0; b < fdof; ++b) {
7525         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7526         const PetscInt ind    = indperm ? indperm[preind] : preind;
7527 
7528         if ((cind < cfdof) && (b == fcdofs[cind])) {
7529           indices[ind] = -(off + foff + b + 1);
7530           ++cind;
7531         } else {
7532           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7533         }
7534       }
7535     }
7536     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7537     foffs[f] += fdof;
7538   }
7539   PetscFunctionReturn(PETSC_SUCCESS);
7540 }
7541 
7542 /*
7543   This version believes the globalSection offsets for each field, rather than just the point offset
7544 
7545  . foffs - The offset into 'indices' for each field, since it is segregated by field
7546 
7547  Notes:
7548  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7549  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7550 */
7551 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7552 {
7553   PetscInt numFields, foff, f;
7554 
7555   PetscFunctionBegin;
7556   PetscCall(PetscSectionGetNumFields(section, &numFields));
7557   for (f = 0; f < numFields; ++f) {
7558     PetscInt        fdof, cfdof;
7559     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7560     PetscInt        cind = 0, b;
7561     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7562 
7563     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7564     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7565     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7566     if (!cfdof) {
7567       for (b = 0; b < fdof; ++b) {
7568         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7569         const PetscInt ind    = indperm ? indperm[preind] : preind;
7570 
7571         indices[ind] = foff + b;
7572       }
7573     } else {
7574       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7575       for (b = 0; b < fdof; ++b) {
7576         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7577         const PetscInt ind    = indperm ? indperm[preind] : preind;
7578 
7579         if ((cind < cfdof) && (b == fcdofs[cind])) {
7580           indices[ind] = -(foff + b + 1);
7581           ++cind;
7582         } else {
7583           indices[ind] = foff + b - cind;
7584         }
7585       }
7586     }
7587     foffs[f] += fdof;
7588   }
7589   PetscFunctionReturn(PETSC_SUCCESS);
7590 }
7591 
7592 static PetscErrorCode DMPlexAnchorsGetSubMatIndices(PetscInt nPoints, const PetscInt pnts[], PetscSection section, PetscSection cSec, PetscInt tmpIndices[], PetscInt fieldOffsets[], PetscInt indices[], const PetscInt ***perms)
7593 {
7594   PetscInt numFields, sStart, sEnd, cStart, cEnd;
7595 
7596   PetscFunctionBegin;
7597   PetscCall(PetscSectionGetNumFields(section, &numFields));
7598   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7599   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7600   for (PetscInt p = 0; p < nPoints; p++) {
7601     PetscInt     b       = pnts[2 * p];
7602     PetscInt     bSecDof = 0, bOff;
7603     PetscInt     cSecDof = 0;
7604     PetscSection indices_section;
7605 
7606     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7607     if (!bSecDof) continue;
7608     if (b >= cStart && b < cEnd) PetscCall(PetscSectionGetDof(cSec, b, &cSecDof));
7609     indices_section = cSecDof > 0 ? cSec : section;
7610     if (numFields) {
7611       PetscInt fStart[32], fEnd[32];
7612 
7613       fStart[0] = 0;
7614       fEnd[0]   = 0;
7615       for (PetscInt f = 0; f < numFields; f++) {
7616         PetscInt fDof = 0;
7617 
7618         PetscCall(PetscSectionGetFieldDof(indices_section, b, f, &fDof));
7619         fStart[f + 1] = fStart[f] + fDof;
7620         fEnd[f + 1]   = fStart[f + 1];
7621       }
7622       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7623       // only apply permutations on one side
7624       PetscCall(DMPlexGetIndicesPointFields_Internal(indices_section, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, perms ? p : -1, NULL, tmpIndices));
7625       for (PetscInt f = 0; f < numFields; f++) {
7626         for (PetscInt i = fStart[f]; i < fEnd[f]; i++) { indices[fieldOffsets[f]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1); }
7627       }
7628     } else {
7629       PetscInt bEnd = 0;
7630 
7631       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7632       PetscCall(DMPlexGetIndicesPoint_Internal(indices_section, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, tmpIndices));
7633 
7634       for (PetscInt i = 0; i < bEnd; i++) indices[fieldOffsets[0]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1);
7635     }
7636   }
7637   PetscFunctionReturn(PETSC_SUCCESS);
7638 }
7639 
7640 PETSC_INTERN PetscErrorCode DMPlexAnchorsGetSubMatModification(DM dm, PetscSection section, PetscInt numPoints, PetscInt numIndices, const PetscInt points[], const PetscInt ***perms, PetscInt *outNumPoints, PetscInt *outNumIndices, PetscInt *outPoints[], PetscInt offsets[], PetscScalar *outMat[])
7641 {
7642   Mat             cMat;
7643   PetscSection    aSec, cSec;
7644   IS              aIS;
7645   PetscInt        aStart = -1, aEnd = -1;
7646   PetscInt        sStart = -1, sEnd = -1;
7647   PetscInt        cStart = -1, cEnd = -1;
7648   const PetscInt *anchors;
7649   PetscInt        numFields, p;
7650   PetscInt        newNumPoints = 0, newNumIndices = 0;
7651   PetscInt       *newPoints, *indices, *newIndices, *tmpIndices, *tmpNewIndices;
7652   PetscInt        oldOffsets[32];
7653   PetscInt        newOffsets[32];
7654   PetscInt        oldOffsetsCopy[32];
7655   PetscInt        newOffsetsCopy[32];
7656   PetscScalar    *modMat         = NULL;
7657   PetscBool       anyConstrained = PETSC_FALSE;
7658 
7659   PetscFunctionBegin;
7660   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7661   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7662   PetscCall(PetscSectionGetNumFields(section, &numFields));
7663 
7664   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7665   /* if there are point-to-point constraints */
7666   if (aSec) {
7667     PetscCall(PetscArrayzero(newOffsets, 32));
7668     PetscCall(PetscArrayzero(oldOffsets, 32));
7669     PetscCall(ISGetIndices(aIS, &anchors));
7670     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7671     PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7672     /* figure out how many points are going to be in the new element matrix
7673      * (we allow double counting, because it's all just going to be summed
7674      * into the global matrix anyway) */
7675     for (p = 0; p < 2 * numPoints; p += 2) {
7676       PetscInt b    = points[p];
7677       PetscInt bDof = 0, bSecDof = 0;
7678 
7679       if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7680       if (!bSecDof) continue;
7681 
7682       for (PetscInt f = 0; f < numFields; f++) {
7683         PetscInt fDof = 0;
7684 
7685         PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7686         oldOffsets[f + 1] += fDof;
7687       }
7688       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7689       if (bDof) {
7690         /* this point is constrained */
7691         /* it is going to be replaced by its anchors */
7692         PetscInt bOff, q;
7693 
7694         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7695         for (q = 0; q < bDof; q++) {
7696           PetscInt a    = anchors[bOff + q];
7697           PetscInt aDof = 0;
7698 
7699           if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7700           if (aDof) {
7701             anyConstrained = PETSC_TRUE;
7702             newNumPoints += 1;
7703           }
7704           newNumIndices += aDof;
7705           for (PetscInt f = 0; f < numFields; ++f) {
7706             PetscInt fDof = 0;
7707 
7708             if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7709             newOffsets[f + 1] += fDof;
7710           }
7711         }
7712       } else {
7713         /* this point is not constrained */
7714         newNumPoints++;
7715         newNumIndices += bSecDof;
7716         for (PetscInt f = 0; f < numFields; ++f) {
7717           PetscInt fDof;
7718 
7719           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7720           newOffsets[f + 1] += fDof;
7721         }
7722       }
7723     }
7724   }
7725   if (!anyConstrained) {
7726     if (outNumPoints) *outNumPoints = 0;
7727     if (outNumIndices) *outNumIndices = 0;
7728     if (outPoints) *outPoints = NULL;
7729     if (outMat) *outMat = NULL;
7730     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7731     PetscFunctionReturn(PETSC_SUCCESS);
7732   }
7733 
7734   if (outNumPoints) *outNumPoints = newNumPoints;
7735   if (outNumIndices) *outNumIndices = newNumIndices;
7736 
7737   for (PetscInt f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7738   for (PetscInt f = 0; f < numFields; ++f) oldOffsets[f + 1] += oldOffsets[f];
7739 
7740   if (!outPoints && !outMat) {
7741     if (offsets) {
7742       for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7743     }
7744     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7745     PetscFunctionReturn(PETSC_SUCCESS);
7746   }
7747 
7748   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7749   PetscCheck(!numFields || oldOffsets[numFields] == numIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, oldOffsets[numFields], numIndices);
7750 
7751   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7752   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7753 
7754   /* output arrays */
7755   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7756   PetscCall(PetscArrayzero(newPoints, 2 * newNumPoints));
7757 
7758   // get the new Points
7759   for (PetscInt p = 0, newP = 0; p < numPoints; p++) {
7760     PetscInt b    = points[2 * p];
7761     PetscInt bDof = 0, bSecDof = 0, bOff;
7762 
7763     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7764     if (!bSecDof) continue;
7765     if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7766     if (bDof) {
7767       PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7768       for (PetscInt q = 0; q < bDof; q++) {
7769         PetscInt a = anchors[bOff + q], aDof = 0;
7770 
7771         if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7772         if (aDof) {
7773           newPoints[2 * newP]     = a;
7774           newPoints[2 * newP + 1] = 0; // orientations are accounted for in constructing the matrix, newly added points are in default orientation
7775           newP++;
7776         }
7777       }
7778     } else {
7779       newPoints[2 * newP]     = b;
7780       newPoints[2 * newP + 1] = points[2 * p + 1];
7781       newP++;
7782     }
7783   }
7784 
7785   if (outMat) {
7786     PetscScalar *tmpMat;
7787     PetscCall(PetscArraycpy(oldOffsetsCopy, oldOffsets, 32));
7788     PetscCall(PetscArraycpy(newOffsetsCopy, newOffsets, 32));
7789 
7790     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &indices));
7791     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7792     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7793     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7794 
7795     for (PetscInt i = 0; i < numIndices; i++) indices[i] = -1;
7796     for (PetscInt i = 0; i < newNumIndices; i++) newIndices[i] = -1;
7797 
7798     PetscCall(DMPlexAnchorsGetSubMatIndices(numPoints, points, section, cSec, tmpIndices, oldOffsetsCopy, indices, perms));
7799     PetscCall(DMPlexAnchorsGetSubMatIndices(newNumPoints, newPoints, section, section, tmpNewIndices, newOffsetsCopy, newIndices, NULL));
7800 
7801     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
7802     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7803     PetscCall(PetscArrayzero(modMat, newNumIndices * numIndices));
7804     // for each field, insert the anchor modification into modMat
7805     for (PetscInt f = 0; f < PetscMax(1, numFields); f++) {
7806       PetscInt fStart    = oldOffsets[f];
7807       PetscInt fNewStart = newOffsets[f];
7808       for (PetscInt p = 0, newP = 0, o = fStart, oNew = fNewStart; p < numPoints; p++) {
7809         PetscInt b    = points[2 * p];
7810         PetscInt bDof = 0, bSecDof = 0, bOff;
7811 
7812         if (b >= sStart && b < sEnd) {
7813           if (numFields) {
7814             PetscCall(PetscSectionGetFieldDof(section, b, f, &bSecDof));
7815           } else {
7816             PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7817           }
7818         }
7819         if (!bSecDof) continue;
7820         if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7821         if (bDof) {
7822           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7823           for (PetscInt q = 0; q < bDof; q++, newP++) {
7824             PetscInt a = anchors[bOff + q], aDof = 0;
7825 
7826             if (a >= sStart && a < sEnd) {
7827               if (numFields) {
7828                 PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
7829               } else {
7830                 PetscCall(PetscSectionGetDof(section, a, &aDof));
7831               }
7832             }
7833             if (aDof) {
7834               PetscCall(MatGetValues(cMat, bSecDof, &indices[o], aDof, &newIndices[oNew], tmpMat));
7835               for (PetscInt d = 0; d < bSecDof; d++) {
7836                 for (PetscInt e = 0; e < aDof; e++) modMat[(o + d) * newNumIndices + oNew + e] = tmpMat[d * aDof + e];
7837               }
7838             }
7839             oNew += aDof;
7840           }
7841         } else {
7842           // Insert the identity matrix in this block
7843           for (PetscInt d = 0; d < bSecDof; d++) modMat[(o + d) * newNumIndices + oNew + d] = 1;
7844           oNew += bSecDof;
7845           newP++;
7846         }
7847         o += bSecDof;
7848       }
7849     }
7850 
7851     *outMat = modMat;
7852 
7853     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7854     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7855     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7856     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7857     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &indices));
7858   }
7859   PetscCall(ISRestoreIndices(aIS, &anchors));
7860 
7861   /* output */
7862   if (outPoints) {
7863     *outPoints = newPoints;
7864   } else {
7865     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7866   }
7867   for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7868   PetscFunctionReturn(PETSC_SUCCESS);
7869 }
7870 
7871 PETSC_INTERN PetscErrorCode DMPlexAnchorsModifyMat_Internal(DM dm, PetscSection section, PetscInt numPoints, PetscInt numIndices, const PetscInt points[], const PetscInt ***perms, PetscInt numRows, PetscInt numCols, const PetscScalar values[], PetscInt *outNumPoints, PetscInt *outNumIndices, PetscInt *outPoints[], PetscScalar *outValues[], PetscInt offsets[], PetscBool multiplyRight, PetscBool multiplyLeft)
7872 {
7873   PetscScalar *modMat        = NULL;
7874   PetscInt     newNumIndices = -1;
7875 
7876   PetscFunctionBegin;
7877   /* If M is the matrix represented by values, get the matrix C such that we will add M * C (or, if multiplyLeft, C^T * M * C) into the global matrix.
7878      modMat is that matrix C */
7879   PetscCall(DMPlexAnchorsGetSubMatModification(dm, section, numPoints, numIndices, points, perms, outNumPoints, &newNumIndices, outPoints, offsets, outValues ? &modMat : NULL));
7880   if (outNumIndices) *outNumIndices = newNumIndices;
7881   if (modMat) {
7882     const PetscScalar *newValues = values;
7883 
7884     if (multiplyRight) {
7885       PetscScalar *newNewValues = NULL;
7886       PetscBLASInt M            = newNumIndices;
7887       PetscBLASInt N            = numRows;
7888       PetscBLASInt K            = numIndices;
7889       PetscScalar  a = 1.0, b = 0.0;
7890 
7891       PetscCheck(numCols == numIndices, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "values matrix has the wrong number of columns: %" PetscInt_FMT ", expected %" PetscInt_FMT, numCols, numIndices);
7892 
7893       PetscCall(DMGetWorkArray(dm, numRows * newNumIndices, MPIU_SCALAR, &newNewValues));
7894       // row-major to column-major conversion, right multiplication becomes left multiplication
7895       PetscCallBLAS("BLASgemm", BLASgemm_("N", "N", &M, &N, &K, &a, modMat, &M, newValues, &K, &b, newNewValues, &M));
7896 
7897       numCols   = newNumIndices;
7898       newValues = newNewValues;
7899     }
7900 
7901     if (multiplyLeft) {
7902       PetscScalar *newNewValues = NULL;
7903       PetscBLASInt M            = numCols;
7904       PetscBLASInt N            = newNumIndices;
7905       PetscBLASInt K            = numIndices;
7906       PetscScalar  a = 1.0, b = 0.0;
7907 
7908       PetscCheck(numRows == numIndices, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "values matrix has the wrong number of rows: %" PetscInt_FMT ", expected %" PetscInt_FMT, numRows, numIndices);
7909 
7910       PetscCall(DMGetWorkArray(dm, newNumIndices * numCols, MPIU_SCALAR, &newNewValues));
7911       // row-major to column-major conversion, left multiplication becomes right multiplication
7912       PetscCallBLAS("BLASgemm", BLASgemm_("N", "T", &M, &N, &K, &a, newValues, &M, modMat, &N, &b, newNewValues, &M));
7913       if (newValues != values) PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &newValues));
7914       newValues = newNewValues;
7915     }
7916     *outValues = (PetscScalar *)newValues;
7917     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
7918   }
7919   PetscFunctionReturn(PETSC_SUCCESS);
7920 }
7921 
7922 PETSC_INTERN 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)
7923 {
7924   PetscFunctionBegin;
7925   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, numPoints, numIndices, points, perms, numIndices, numIndices, values, outNumPoints, outNumIndices, outPoints, outValues, offsets, PETSC_TRUE, multiplyLeft));
7926   PetscFunctionReturn(PETSC_SUCCESS);
7927 }
7928 
7929 static PetscErrorCode DMPlexGetClosureIndicesSize_Internal(DM dm, PetscSection section, PetscInt point, PetscInt *closureSize)
7930 {
7931   /* Closure ordering */
7932   PetscSection    clSection;
7933   IS              clPoints;
7934   const PetscInt *clp;
7935   PetscInt       *points;
7936   PetscInt        Ncl, Ni = 0;
7937 
7938   PetscFunctionBeginHot;
7939   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7940   for (PetscInt p = 0; p < Ncl * 2; p += 2) {
7941     PetscInt dof;
7942 
7943     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7944     Ni += dof;
7945   }
7946   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7947   *closureSize = Ni;
7948   PetscFunctionReturn(PETSC_SUCCESS);
7949 }
7950 
7951 static PetscErrorCode DMPlexGetClosureIndices_Internal(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numRows, PetscInt *numCols, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[], PetscBool multiplyRight, PetscBool multiplyLeft)
7952 {
7953   /* Closure ordering */
7954   PetscSection    clSection;
7955   IS              clPoints;
7956   const PetscInt *clp;
7957   PetscInt       *points;
7958   const PetscInt *clperm = NULL;
7959   /* Dof permutation and sign flips */
7960   const PetscInt    **perms[32] = {NULL};
7961   const PetscScalar **flips[32] = {NULL};
7962   PetscScalar        *valCopy   = NULL;
7963   /* Hanging node constraints */
7964   PetscInt    *pointsC = NULL;
7965   PetscScalar *valuesC = NULL;
7966   PetscInt     NclC, NiC;
7967 
7968   PetscInt *idx;
7969   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7970   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7971   PetscInt  idxStart, idxEnd;
7972   PetscInt  nRows, nCols;
7973 
7974   PetscFunctionBeginHot;
7975   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7976   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7977   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7978   PetscAssertPointer(numRows, 6);
7979   PetscAssertPointer(numCols, 7);
7980   if (indices) PetscAssertPointer(indices, 8);
7981   if (outOffsets) PetscAssertPointer(outOffsets, 9);
7982   if (values) PetscAssertPointer(values, 10);
7983   PetscCall(PetscSectionGetNumFields(section, &Nf));
7984   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7985   PetscCall(PetscArrayzero(offsets, 32));
7986   /* 1) Get points in closure */
7987   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7988   if (useClPerm) {
7989     PetscInt depth, clsize;
7990     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7991     for (clsize = 0, p = 0; p < Ncl; p++) {
7992       PetscInt dof;
7993       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7994       clsize += dof;
7995     }
7996     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7997   }
7998   /* 2) Get number of indices on these points and field offsets from section */
7999   for (p = 0; p < Ncl * 2; p += 2) {
8000     PetscInt dof, fdof;
8001 
8002     PetscCall(PetscSectionGetDof(section, points[p], &dof));
8003     for (f = 0; f < Nf; ++f) {
8004       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
8005       offsets[f + 1] += fdof;
8006     }
8007     Ni += dof;
8008   }
8009   if (*numRows == -1) *numRows = Ni;
8010   if (*numCols == -1) *numCols = Ni;
8011   nRows = *numRows;
8012   nCols = *numCols;
8013   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
8014   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
8015   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
8016   if (multiplyRight) PetscCheck(nCols == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " columns, got %" PetscInt_FMT, Ni, nCols);
8017   if (multiplyLeft) PetscCheck(nRows == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " rows, got %" PetscInt_FMT, Ni, nRows);
8018   for (f = 0; f < PetscMax(1, Nf); ++f) {
8019     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8020     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
8021     /* may need to apply sign changes to the element matrix */
8022     if (values && flips[f]) {
8023       PetscInt foffset = offsets[f];
8024 
8025       for (p = 0; p < Ncl; ++p) {
8026         PetscInt           pnt  = points[2 * p], fdof;
8027         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
8028 
8029         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
8030         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
8031         if (flip) {
8032           PetscInt i, j, k;
8033 
8034           if (!valCopy) {
8035             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8036             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
8037             *values = valCopy;
8038           }
8039           for (i = 0; i < fdof; ++i) {
8040             PetscScalar fval = flip[i];
8041 
8042             if (multiplyRight) {
8043               for (k = 0; k < nRows; ++k) { valCopy[Ni * k + (foffset + i)] *= fval; }
8044             }
8045             if (multiplyLeft) {
8046               for (k = 0; k < nCols; ++k) { valCopy[nCols * (foffset + i) + k] *= fval; }
8047             }
8048           }
8049         }
8050         foffset += fdof;
8051       }
8052     }
8053   }
8054   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
8055   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, Ncl, Ni, points, perms, nRows, nCols, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, multiplyRight, multiplyLeft));
8056   if (NclC) {
8057     if (multiplyRight) { *numCols = nCols = NiC; }
8058     if (multiplyLeft) { *numRows = nRows = NiC; }
8059     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8060     for (f = 0; f < PetscMax(1, Nf); ++f) {
8061       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8062       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8063     }
8064     for (f = 0; f < PetscMax(1, Nf); ++f) {
8065       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
8066       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
8067     }
8068     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8069     Ncl    = NclC;
8070     Ni     = NiC;
8071     points = pointsC;
8072     if (values) *values = valuesC;
8073   }
8074   /* 5) Calculate indices */
8075   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
8076   PetscCall(PetscSectionGetChart(idxSection, &idxStart, &idxEnd));
8077   if (Nf) {
8078     PetscInt  idxOff;
8079     PetscBool useFieldOffsets;
8080 
8081     if (outOffsets) {
8082       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
8083     }
8084     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
8085     if (useFieldOffsets) {
8086       for (p = 0; p < Ncl; ++p) {
8087         const PetscInt pnt = points[p * 2];
8088 
8089         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
8090       }
8091     } else {
8092       for (p = 0; p < Ncl; ++p) {
8093         const PetscInt pnt = points[p * 2];
8094 
8095         if (pnt < idxStart || pnt >= idxEnd) continue;
8096         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8097         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8098          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
8099          * global section. */
8100         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
8101       }
8102     }
8103   } else {
8104     PetscInt off = 0, idxOff;
8105 
8106     for (p = 0; p < Ncl; ++p) {
8107       const PetscInt  pnt  = points[p * 2];
8108       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
8109 
8110       if (pnt < idxStart || pnt >= idxEnd) continue;
8111       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8112       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8113        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
8114       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
8115     }
8116   }
8117   /* 6) Cleanup */
8118   for (f = 0; f < PetscMax(1, Nf); ++f) {
8119     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8120     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8121   }
8122   if (NclC) {
8123     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
8124   } else {
8125     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8126   }
8127 
8128   if (indices) *indices = idx;
8129   PetscFunctionReturn(PETSC_SUCCESS);
8130 }
8131 
8132 /*@C
8133   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
8134 
8135   Not collective
8136 
8137   Input Parameters:
8138 + dm         - The `DM`
8139 . section    - The `PetscSection` describing the points (a local section)
8140 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8141 . point      - The point defining the closure
8142 - useClPerm  - Use the closure point permutation if available
8143 
8144   Output Parameters:
8145 + numIndices - The number of dof indices in the closure of point with the input sections
8146 . indices    - The dof indices
8147 . outOffsets - Array to write the field offsets into, or `NULL`
8148 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8149 
8150   Level: advanced
8151 
8152   Notes:
8153   Call `DMPlexRestoreClosureIndices()` to free allocated memory
8154 
8155   If `idxSection` is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8156   of those indices is not significant.  If `idxSection` is local, the constrained dofs will yield the involution -(idx+1)
8157   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8158   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when `idxSection` == section, otherwise global
8159   indices (with the above semantics) are implied.
8160 
8161 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
8162           `PetscSection`, `DMGetGlobalSection()`
8163 @*/
8164 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
8165 {
8166   PetscInt numRows = -1, numCols = -1;
8167 
8168   PetscFunctionBeginHot;
8169   PetscCall(DMPlexGetClosureIndices_Internal(dm, section, idxSection, point, useClPerm, &numRows, &numCols, indices, outOffsets, values, PETSC_TRUE, PETSC_TRUE));
8170   PetscCheck(numRows == numCols, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Symmetric matrix transformation produces rectangular dimensions (%" PetscInt_FMT ", %" PetscInt_FMT ")", numRows, numCols);
8171   *numIndices = numRows;
8172   PetscFunctionReturn(PETSC_SUCCESS);
8173 }
8174 
8175 /*@C
8176   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
8177 
8178   Not collective
8179 
8180   Input Parameters:
8181 + dm         - The `DM`
8182 . section    - The `PetscSection` describing the points (a local section)
8183 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8184 . point      - The point defining the closure
8185 - useClPerm  - Use the closure point permutation if available
8186 
8187   Output Parameters:
8188 + numIndices - The number of dof indices in the closure of point with the input sections
8189 . indices    - The dof indices
8190 . outOffsets - Array to write the field offsets into, or `NULL`
8191 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8192 
8193   Level: advanced
8194 
8195   Notes:
8196   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
8197 
8198   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8199   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
8200   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8201   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
8202   indices (with the above semantics) are implied.
8203 
8204 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
8205 @*/
8206 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
8207 {
8208   PetscFunctionBegin;
8209   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8210   PetscAssertPointer(indices, 7);
8211   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
8212   PetscFunctionReturn(PETSC_SUCCESS);
8213 }
8214 
8215 PetscErrorCode DMPlexMatSetClosure_Internal(DM dm, PetscSection section, PetscSection globalSection, PetscBool useClPerm, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8216 {
8217   DM_Plex           *mesh = (DM_Plex *)dm->data;
8218   PetscInt          *indices;
8219   PetscInt           numIndices;
8220   const PetscScalar *valuesOrig = values;
8221   PetscErrorCode     ierr;
8222 
8223   PetscFunctionBegin;
8224   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8225   if (!section) PetscCall(DMGetLocalSection(dm, &section));
8226   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
8227   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
8228   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
8229   PetscValidHeaderSpecific(A, MAT_CLASSID, 5);
8230 
8231   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, useClPerm, &numIndices, &indices, NULL, (PetscScalar **)&values));
8232 
8233   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
8234   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8235   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
8236   if (ierr) {
8237     PetscMPIInt rank;
8238 
8239     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8240     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8241     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
8242     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8243     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8244     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
8245   }
8246   if (mesh->printFEM > 1) {
8247     PetscInt i;
8248     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
8249     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
8250     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8251   }
8252 
8253   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8254   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8255   PetscFunctionReturn(PETSC_SUCCESS);
8256 }
8257 
8258 /*@C
8259   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
8260 
8261   Not collective
8262 
8263   Input Parameters:
8264 + dm            - The `DM`
8265 . section       - The section describing the layout in `v`, or `NULL` to use the default section
8266 . globalSection - The section describing the layout in `v`, or `NULL` to use the default global section
8267 . A             - The matrix
8268 . point         - The point in the `DM`
8269 . values        - The array of values
8270 - mode          - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8271 
8272   Level: intermediate
8273 
8274 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8275 @*/
8276 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8277 {
8278   PetscFunctionBegin;
8279   PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, PETSC_TRUE, A, point, values, mode));
8280   PetscFunctionReturn(PETSC_SUCCESS);
8281 }
8282 
8283 /*@C
8284   DMPlexMatSetClosureGeneral - Set an array of the values on the closure of 'point' using a different row and column section
8285 
8286   Not collective
8287 
8288   Input Parameters:
8289 + dmRow            - The `DM` for the row fields
8290 . sectionRow       - The section describing the layout, or `NULL` to use the default section in `dmRow`
8291 . useRowPerm       - The flag to use the closure permutation of the `dmRow` if available
8292 . globalSectionRow - The section describing the layout, or `NULL` to use the default global section in `dmRow`
8293 . dmCol            - The `DM` for the column fields
8294 . sectionCol       - The section describing the layout, or `NULL` to use the default section in `dmCol`
8295 . useColPerm       - The flag to use the closure permutation of the `dmCol` if available
8296 . globalSectionCol - The section describing the layout, or `NULL` to use the default global section in `dmCol`
8297 . A                - The matrix
8298 . point            - The point in the `DM`
8299 . values           - The array of values
8300 - mode             - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8301 
8302   Level: intermediate
8303 
8304 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8305 @*/
8306 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)
8307 {
8308   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
8309   PetscInt          *indicesRow, *indicesCol;
8310   PetscInt           numIndicesRow = -1, numIndicesCol = -1;
8311   const PetscScalar *valuesV0 = values, *valuesV1, *valuesV2;
8312 
8313   PetscErrorCode ierr;
8314 
8315   PetscFunctionBegin;
8316   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
8317   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
8318   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
8319   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
8320   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
8321   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 5);
8322   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
8323   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 6);
8324   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
8325   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 7);
8326   PetscValidHeaderSpecific(A, MAT_CLASSID, 9);
8327 
8328   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmRow, sectionRow, point, &numIndicesRow));
8329   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmCol, sectionCol, point, &numIndicesCol));
8330   valuesV1 = valuesV0;
8331   PetscCall(DMPlexGetClosureIndices_Internal(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV1, PETSC_FALSE, PETSC_TRUE));
8332   valuesV2 = valuesV1;
8333   PetscCall(DMPlexGetClosureIndices_Internal(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesRow, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2, PETSC_TRUE, PETSC_FALSE));
8334 
8335   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2));
8336   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8337   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2, mode);
8338   if (ierr) {
8339     PetscMPIInt rank;
8340 
8341     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8342     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8343     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
8344     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV2));
8345     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8346     if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8347     if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8348   }
8349 
8350   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2));
8351   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8352   if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8353   if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8354   PetscFunctionReturn(PETSC_SUCCESS);
8355 }
8356 
8357 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8358 {
8359   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
8360   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8361   PetscInt       *cpoints = NULL;
8362   PetscInt       *findices, *cindices;
8363   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8364   PetscInt        foffsets[32], coffsets[32];
8365   DMPolytopeType  ct;
8366   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8367   PetscErrorCode  ierr;
8368 
8369   PetscFunctionBegin;
8370   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8371   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8372   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8373   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8374   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8375   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8376   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8377   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8378   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8379   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8380   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
8381   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8382   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8383   PetscCall(PetscArrayzero(foffsets, 32));
8384   PetscCall(PetscArrayzero(coffsets, 32));
8385   /* Column indices */
8386   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8387   maxFPoints = numCPoints;
8388   /* Compress out points not in the section */
8389   /*   TODO: Squeeze out points with 0 dof as well */
8390   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8391   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8392     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8393       cpoints[q * 2]     = cpoints[p];
8394       cpoints[q * 2 + 1] = cpoints[p + 1];
8395       ++q;
8396     }
8397   }
8398   numCPoints = q;
8399   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8400     PetscInt fdof;
8401 
8402     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8403     if (!dof) continue;
8404     for (f = 0; f < numFields; ++f) {
8405       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8406       coffsets[f + 1] += fdof;
8407     }
8408     numCIndices += dof;
8409   }
8410   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8411   /* Row indices */
8412   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8413   {
8414     DMPlexTransform tr;
8415     DMPolytopeType *rct;
8416     PetscInt       *rsize, *rcone, *rornt, Nt;
8417 
8418     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8419     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8420     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8421     numSubcells = rsize[Nt - 1];
8422     PetscCall(DMPlexTransformDestroy(&tr));
8423   }
8424   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8425   for (r = 0, q = 0; r < numSubcells; ++r) {
8426     /* TODO Map from coarse to fine cells */
8427     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8428     /* Compress out points not in the section */
8429     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8430     for (p = 0; p < numFPoints * 2; p += 2) {
8431       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8432         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8433         if (!dof) continue;
8434         for (s = 0; s < q; ++s)
8435           if (fpoints[p] == ftotpoints[s * 2]) break;
8436         if (s < q) continue;
8437         ftotpoints[q * 2]     = fpoints[p];
8438         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8439         ++q;
8440       }
8441     }
8442     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8443   }
8444   numFPoints = q;
8445   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8446     PetscInt fdof;
8447 
8448     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8449     if (!dof) continue;
8450     for (f = 0; f < numFields; ++f) {
8451       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8452       foffsets[f + 1] += fdof;
8453     }
8454     numFIndices += dof;
8455   }
8456   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8457 
8458   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8459   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8460   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8461   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8462   if (numFields) {
8463     const PetscInt **permsF[32] = {NULL};
8464     const PetscInt **permsC[32] = {NULL};
8465 
8466     for (f = 0; f < numFields; f++) {
8467       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8468       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8469     }
8470     for (p = 0; p < numFPoints; p++) {
8471       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8472       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8473     }
8474     for (p = 0; p < numCPoints; p++) {
8475       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8476       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8477     }
8478     for (f = 0; f < numFields; f++) {
8479       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8480       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8481     }
8482   } else {
8483     const PetscInt **permsF = NULL;
8484     const PetscInt **permsC = NULL;
8485 
8486     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8487     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8488     for (p = 0, off = 0; p < numFPoints; p++) {
8489       const PetscInt *perm = permsF ? permsF[p] : NULL;
8490 
8491       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8492       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8493     }
8494     for (p = 0, off = 0; p < numCPoints; p++) {
8495       const PetscInt *perm = permsC ? permsC[p] : NULL;
8496 
8497       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8498       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8499     }
8500     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8501     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8502   }
8503   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8504   /* TODO: flips */
8505   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8506   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8507   if (ierr) {
8508     PetscMPIInt rank;
8509 
8510     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8511     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8512     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8513     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8514     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8515   }
8516   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8517   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8518   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8519   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8520   PetscFunctionReturn(PETSC_SUCCESS);
8521 }
8522 
8523 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8524 {
8525   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8526   PetscInt       *cpoints      = NULL;
8527   PetscInt        foffsets[32] = {0}, coffsets[32] = {0};
8528   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8529   DMPolytopeType  ct;
8530   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8531 
8532   PetscFunctionBegin;
8533   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8534   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8535   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8536   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8537   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8538   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8539   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8540   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8541   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8542   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8543   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8544   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8545   /* Column indices */
8546   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8547   maxFPoints = numCPoints;
8548   /* Compress out points not in the section */
8549   /*   TODO: Squeeze out points with 0 dof as well */
8550   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8551   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8552     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8553       cpoints[q * 2]     = cpoints[p];
8554       cpoints[q * 2 + 1] = cpoints[p + 1];
8555       ++q;
8556     }
8557   }
8558   numCPoints = q;
8559   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8560     PetscInt fdof;
8561 
8562     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8563     if (!dof) continue;
8564     for (f = 0; f < numFields; ++f) {
8565       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8566       coffsets[f + 1] += fdof;
8567     }
8568     numCIndices += dof;
8569   }
8570   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8571   /* Row indices */
8572   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8573   {
8574     DMPlexTransform tr;
8575     DMPolytopeType *rct;
8576     PetscInt       *rsize, *rcone, *rornt, Nt;
8577 
8578     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8579     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8580     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8581     numSubcells = rsize[Nt - 1];
8582     PetscCall(DMPlexTransformDestroy(&tr));
8583   }
8584   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8585   for (r = 0, q = 0; r < numSubcells; ++r) {
8586     /* TODO Map from coarse to fine cells */
8587     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8588     /* Compress out points not in the section */
8589     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8590     for (p = 0; p < numFPoints * 2; p += 2) {
8591       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8592         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8593         if (!dof) continue;
8594         for (s = 0; s < q; ++s)
8595           if (fpoints[p] == ftotpoints[s * 2]) break;
8596         if (s < q) continue;
8597         ftotpoints[q * 2]     = fpoints[p];
8598         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8599         ++q;
8600       }
8601     }
8602     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8603   }
8604   numFPoints = q;
8605   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8606     PetscInt fdof;
8607 
8608     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8609     if (!dof) continue;
8610     for (f = 0; f < numFields; ++f) {
8611       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8612       foffsets[f + 1] += fdof;
8613     }
8614     numFIndices += dof;
8615   }
8616   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8617 
8618   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8619   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8620   if (numFields) {
8621     const PetscInt **permsF[32] = {NULL};
8622     const PetscInt **permsC[32] = {NULL};
8623 
8624     for (f = 0; f < numFields; f++) {
8625       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8626       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8627     }
8628     for (p = 0; p < numFPoints; p++) {
8629       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8630       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8631     }
8632     for (p = 0; p < numCPoints; p++) {
8633       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8634       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8635     }
8636     for (f = 0; f < numFields; f++) {
8637       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8638       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8639     }
8640   } else {
8641     const PetscInt **permsF = NULL;
8642     const PetscInt **permsC = NULL;
8643 
8644     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8645     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8646     for (p = 0, off = 0; p < numFPoints; p++) {
8647       const PetscInt *perm = permsF ? permsF[p] : NULL;
8648 
8649       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8650       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8651     }
8652     for (p = 0, off = 0; p < numCPoints; p++) {
8653       const PetscInt *perm = permsC ? permsC[p] : NULL;
8654 
8655       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8656       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8657     }
8658     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8659     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8660   }
8661   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8662   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8663   PetscFunctionReturn(PETSC_SUCCESS);
8664 }
8665 
8666 /*@
8667   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8668 
8669   Input Parameter:
8670 . dm - The `DMPLEX` object
8671 
8672   Output Parameter:
8673 . cellHeight - The height of a cell
8674 
8675   Level: developer
8676 
8677 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetVTKCellHeight()`
8678 @*/
8679 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8680 {
8681   DM_Plex *mesh = (DM_Plex *)dm->data;
8682 
8683   PetscFunctionBegin;
8684   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8685   PetscAssertPointer(cellHeight, 2);
8686   *cellHeight = mesh->vtkCellHeight;
8687   PetscFunctionReturn(PETSC_SUCCESS);
8688 }
8689 
8690 /*@
8691   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8692 
8693   Input Parameters:
8694 + dm         - The `DMPLEX` object
8695 - cellHeight - The height of a cell
8696 
8697   Level: developer
8698 
8699 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8700 @*/
8701 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8702 {
8703   DM_Plex *mesh = (DM_Plex *)dm->data;
8704 
8705   PetscFunctionBegin;
8706   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8707   mesh->vtkCellHeight = cellHeight;
8708   PetscFunctionReturn(PETSC_SUCCESS);
8709 }
8710 
8711 /*@
8712   DMPlexGetCellTypeStratum - Get the range of cells of a given celltype
8713 
8714   Input Parameters:
8715 + dm - The `DMPLEX` object
8716 - ct - The `DMPolytopeType` of the cell
8717 
8718   Output Parameters:
8719 + start - The first cell of this type, or `NULL`
8720 - end   - The upper bound on this celltype, or `NULL`
8721 
8722   Level: advanced
8723 
8724 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8725 @*/
8726 PetscErrorCode DMPlexGetCellTypeStratum(DM dm, DMPolytopeType ct, PetscInt *start, PetscInt *end)
8727 {
8728   DM_Plex *mesh = (DM_Plex *)dm->data;
8729   DMLabel  label;
8730   PetscInt pStart, pEnd;
8731 
8732   PetscFunctionBegin;
8733   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8734   if (start) {
8735     PetscAssertPointer(start, 3);
8736     *start = 0;
8737   }
8738   if (end) {
8739     PetscAssertPointer(end, 4);
8740     *end = 0;
8741   }
8742   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8743   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
8744   if (mesh->tr) {
8745     PetscCall(DMPlexTransformGetCellTypeStratum(mesh->tr, ct, start, end));
8746   } else {
8747     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
8748     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named celltype was found");
8749     PetscCall(DMLabelGetStratumBounds(label, ct, start, end));
8750   }
8751   PetscFunctionReturn(PETSC_SUCCESS);
8752 }
8753 
8754 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8755 {
8756   PetscSection section, globalSection;
8757   PetscInt    *numbers, p;
8758 
8759   PetscFunctionBegin;
8760   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8761   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8762   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8763   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8764   PetscCall(PetscSectionSetUp(section));
8765   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &globalSection));
8766   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8767   for (p = pStart; p < pEnd; ++p) {
8768     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8769     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8770     else numbers[p - pStart] += shift;
8771   }
8772   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8773   if (globalSize) {
8774     PetscLayout layout;
8775     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8776     PetscCall(PetscLayoutGetSize(layout, globalSize));
8777     PetscCall(PetscLayoutDestroy(&layout));
8778   }
8779   PetscCall(PetscSectionDestroy(&section));
8780   PetscCall(PetscSectionDestroy(&globalSection));
8781   PetscFunctionReturn(PETSC_SUCCESS);
8782 }
8783 
8784 /*@
8785   DMPlexCreateCellNumbering - Get a global cell numbering for all cells on this process
8786 
8787   Input Parameters:
8788 + dm         - The `DMPLEX` object
8789 - includeAll - Whether to include all cells, or just the simplex and box cells
8790 
8791   Output Parameter:
8792 . globalCellNumbers - Global cell numbers for all cells on this process
8793 
8794   Level: developer
8795 
8796 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`
8797 @*/
8798 PetscErrorCode DMPlexCreateCellNumbering(DM dm, PetscBool includeAll, IS *globalCellNumbers)
8799 {
8800   PetscInt cellHeight, cStart, cEnd;
8801 
8802   PetscFunctionBegin;
8803   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8804   if (includeAll) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8805   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8806   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8807   PetscFunctionReturn(PETSC_SUCCESS);
8808 }
8809 
8810 /*@
8811   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8812 
8813   Input Parameter:
8814 . dm - The `DMPLEX` object
8815 
8816   Output Parameter:
8817 . globalCellNumbers - Global cell numbers for all cells on this process
8818 
8819   Level: developer
8820 
8821 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateCellNumbering()`, `DMPlexGetVertexNumbering()`
8822 @*/
8823 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8824 {
8825   DM_Plex *mesh = (DM_Plex *)dm->data;
8826 
8827   PetscFunctionBegin;
8828   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8829   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8830   *globalCellNumbers = mesh->globalCellNumbers;
8831   PetscFunctionReturn(PETSC_SUCCESS);
8832 }
8833 
8834 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8835 {
8836   PetscInt vStart, vEnd;
8837 
8838   PetscFunctionBegin;
8839   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8840   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8841   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8842   PetscFunctionReturn(PETSC_SUCCESS);
8843 }
8844 
8845 /*@
8846   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8847 
8848   Input Parameter:
8849 . dm - The `DMPLEX` object
8850 
8851   Output Parameter:
8852 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8853 
8854   Level: developer
8855 
8856 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8857 @*/
8858 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8859 {
8860   DM_Plex *mesh = (DM_Plex *)dm->data;
8861 
8862   PetscFunctionBegin;
8863   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8864   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8865   *globalVertexNumbers = mesh->globalVertexNumbers;
8866   PetscFunctionReturn(PETSC_SUCCESS);
8867 }
8868 
8869 /*@
8870   DMPlexCreatePointNumbering - Create a global numbering for all points.
8871 
8872   Collective
8873 
8874   Input Parameter:
8875 . dm - The `DMPLEX` object
8876 
8877   Output Parameter:
8878 . globalPointNumbers - Global numbers for all points on this process
8879 
8880   Level: developer
8881 
8882   Notes:
8883   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8884   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8885   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8886   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8887 
8888   The partitioned mesh is
8889   ```
8890   (2)--0--(3)--1--(4)    (1)--0--(2)
8891   ```
8892   and its global numbering is
8893   ```
8894   (3)--0--(4)--1--(5)--2--(6)
8895   ```
8896   Then the global numbering is provided as
8897   ```
8898   [0] Number of indices in set 5
8899   [0] 0 0
8900   [0] 1 1
8901   [0] 2 3
8902   [0] 3 4
8903   [0] 4 -6
8904   [1] Number of indices in set 3
8905   [1] 0 2
8906   [1] 1 5
8907   [1] 2 6
8908   ```
8909 
8910 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8911 @*/
8912 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8913 {
8914   IS        nums[4];
8915   PetscInt  depths[4], gdepths[4], starts[4];
8916   PetscInt  depth, d, shift = 0;
8917   PetscBool empty = PETSC_FALSE;
8918 
8919   PetscFunctionBegin;
8920   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8921   PetscCall(DMPlexGetDepth(dm, &depth));
8922   // For unstratified meshes use dim instead of depth
8923   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8924   // If any stratum is empty, we must mark all empty
8925   for (d = 0; d <= depth; ++d) {
8926     PetscInt end;
8927 
8928     depths[d] = depth - d;
8929     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8930     if (!(starts[d] - end)) empty = PETSC_TRUE;
8931   }
8932   if (empty)
8933     for (d = 0; d <= depth; ++d) {
8934       depths[d] = -1;
8935       starts[d] = -1;
8936     }
8937   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8938   PetscCall(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8939   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]);
8940   // Note here that 'shift' is collective, so that the numbering is stratified by depth
8941   for (d = 0; d <= depth; ++d) {
8942     PetscInt pStart, pEnd, gsize;
8943 
8944     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8945     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8946     shift += gsize;
8947   }
8948   PetscCall(ISConcatenate(PETSC_COMM_SELF, depth + 1, nums, globalPointNumbers));
8949   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8950   PetscFunctionReturn(PETSC_SUCCESS);
8951 }
8952 
8953 /*@
8954   DMPlexCreateEdgeNumbering - Create a global numbering for edges.
8955 
8956   Collective
8957 
8958   Input Parameter:
8959 . dm - The `DMPLEX` object
8960 
8961   Output Parameter:
8962 . globalEdgeNumbers - Global numbers for all edges on this process
8963 
8964   Level: developer
8965 
8966   Notes:
8967   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). In the IS, owned edges will have their non-negative value while edges owned by different ranks will be involuted -(idx+1).
8968 
8969 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`, `DMPlexCreatePointNumbering()`
8970 @*/
8971 PetscErrorCode DMPlexCreateEdgeNumbering(DM dm, IS *globalEdgeNumbers)
8972 {
8973   PetscSF  sf;
8974   PetscInt eStart, eEnd;
8975 
8976   PetscFunctionBegin;
8977   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8978   PetscCall(DMGetPointSF(dm, &sf));
8979   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
8980   PetscCall(DMPlexCreateNumbering_Plex(dm, eStart, eEnd, 0, NULL, sf, globalEdgeNumbers));
8981   PetscFunctionReturn(PETSC_SUCCESS);
8982 }
8983 
8984 /*@
8985   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8986 
8987   Input Parameter:
8988 . dm - The `DMPLEX` object
8989 
8990   Output Parameter:
8991 . ranks - The rank field
8992 
8993   Options Database Key:
8994 . -dm_partition_view - Adds the rank field into the `DM` output from `-dm_view` using the same viewer
8995 
8996   Level: intermediate
8997 
8998 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8999 @*/
9000 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
9001 {
9002   DM             rdm;
9003   PetscFE        fe;
9004   PetscScalar   *r;
9005   PetscMPIInt    rank;
9006   DMPolytopeType ct;
9007   PetscInt       dim, cStart, cEnd, c;
9008   PetscBool      simplex;
9009 
9010   PetscFunctionBeginUser;
9011   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9012   PetscAssertPointer(ranks, 2);
9013   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
9014   PetscCall(DMClone(dm, &rdm));
9015   PetscCall(DMGetDimension(rdm, &dim));
9016   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
9017   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
9018   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
9019   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
9020   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
9021   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
9022   PetscCall(PetscFEDestroy(&fe));
9023   PetscCall(DMCreateDS(rdm));
9024   PetscCall(DMCreateGlobalVector(rdm, ranks));
9025   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
9026   PetscCall(VecGetArray(*ranks, &r));
9027   for (c = cStart; c < cEnd; ++c) {
9028     PetscScalar *lr;
9029 
9030     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
9031     if (lr) *lr = rank;
9032   }
9033   PetscCall(VecRestoreArray(*ranks, &r));
9034   PetscCall(DMDestroy(&rdm));
9035   PetscFunctionReturn(PETSC_SUCCESS);
9036 }
9037 
9038 /*@
9039   DMPlexCreateLabelField - Create a field whose value is the label value for that point
9040 
9041   Input Parameters:
9042 + dm    - The `DMPLEX`
9043 - label - The `DMLabel`
9044 
9045   Output Parameter:
9046 . val - The label value field
9047 
9048   Options Database Key:
9049 . -dm_label_view - Adds the label value field into the `DM` output from `-dm_view` using the same viewer
9050 
9051   Level: intermediate
9052 
9053 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
9054 @*/
9055 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
9056 {
9057   DM             rdm, plex;
9058   Vec            lval;
9059   PetscSection   section;
9060   PetscFE        fe;
9061   PetscScalar   *v;
9062   PetscInt       dim, pStart, pEnd, p, cStart;
9063   DMPolytopeType ct;
9064   char           name[PETSC_MAX_PATH_LEN];
9065   const char    *lname, *prefix;
9066 
9067   PetscFunctionBeginUser;
9068   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9069   PetscAssertPointer(label, 2);
9070   PetscAssertPointer(val, 3);
9071   PetscCall(DMClone(dm, &rdm));
9072   PetscCall(DMConvert(rdm, DMPLEX, &plex));
9073   PetscCall(DMPlexGetHeightStratum(plex, 0, &cStart, NULL));
9074   PetscCall(DMPlexGetCellType(plex, cStart, &ct));
9075   PetscCall(DMDestroy(&plex));
9076   PetscCall(DMGetDimension(rdm, &dim));
9077   PetscCall(DMGetOptionsPrefix(dm, &prefix));
9078   PetscCall(PetscObjectGetName((PetscObject)label, &lname));
9079   PetscCall(PetscSNPrintf(name, sizeof(name), "%s%s_", prefix ? prefix : "", lname));
9080   PetscCall(PetscFECreateByCell(PETSC_COMM_SELF, dim, 1, ct, name, -1, &fe));
9081   PetscCall(PetscObjectSetName((PetscObject)fe, ""));
9082   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
9083   PetscCall(PetscFEDestroy(&fe));
9084   PetscCall(DMCreateDS(rdm));
9085   PetscCall(DMCreateGlobalVector(rdm, val));
9086   PetscCall(DMCreateLocalVector(rdm, &lval));
9087   PetscCall(PetscObjectSetName((PetscObject)*val, lname));
9088   PetscCall(DMGetLocalSection(rdm, &section));
9089   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
9090   PetscCall(VecGetArray(lval, &v));
9091   for (p = pStart; p < pEnd; ++p) {
9092     PetscInt cval, dof, off;
9093 
9094     PetscCall(PetscSectionGetDof(section, p, &dof));
9095     if (!dof) continue;
9096     PetscCall(DMLabelGetValue(label, p, &cval));
9097     PetscCall(PetscSectionGetOffset(section, p, &off));
9098     for (PetscInt d = 0; d < dof; d++) v[off + d] = cval;
9099   }
9100   PetscCall(VecRestoreArray(lval, &v));
9101   PetscCall(DMLocalToGlobal(rdm, lval, INSERT_VALUES, *val));
9102   PetscCall(VecDestroy(&lval));
9103   PetscCall(DMDestroy(&rdm));
9104   PetscFunctionReturn(PETSC_SUCCESS);
9105 }
9106 
9107 /*@
9108   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
9109 
9110   Input Parameter:
9111 . dm - The `DMPLEX` object
9112 
9113   Level: developer
9114 
9115   Notes:
9116   This is a useful diagnostic when creating meshes programmatically.
9117 
9118   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9119 
9120 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9121 @*/
9122 PetscErrorCode DMPlexCheckSymmetry(DM dm)
9123 {
9124   PetscSection    coneSection, supportSection;
9125   const PetscInt *cone, *support;
9126   PetscInt        coneSize, c, supportSize, s;
9127   PetscInt        pStart, pEnd, p, pp, csize, ssize;
9128   PetscBool       storagecheck = PETSC_TRUE;
9129 
9130   PetscFunctionBegin;
9131   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9132   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
9133   PetscCall(DMPlexGetConeSection(dm, &coneSection));
9134   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
9135   /* Check that point p is found in the support of its cone points, and vice versa */
9136   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9137   for (p = pStart; p < pEnd; ++p) {
9138     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
9139     PetscCall(DMPlexGetCone(dm, p, &cone));
9140     for (c = 0; c < coneSize; ++c) {
9141       PetscBool dup = PETSC_FALSE;
9142       PetscInt  d;
9143       for (d = c - 1; d >= 0; --d) {
9144         if (cone[c] == cone[d]) {
9145           dup = PETSC_TRUE;
9146           break;
9147         }
9148       }
9149       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
9150       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
9151       for (s = 0; s < supportSize; ++s) {
9152         if (support[s] == p) break;
9153       }
9154       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
9155         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
9156         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
9157         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9158         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
9159         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
9160         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9161         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]);
9162         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
9163       }
9164     }
9165     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
9166     if (p != pp) {
9167       storagecheck = PETSC_FALSE;
9168       continue;
9169     }
9170     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
9171     PetscCall(DMPlexGetSupport(dm, p, &support));
9172     for (s = 0; s < supportSize; ++s) {
9173       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
9174       PetscCall(DMPlexGetCone(dm, support[s], &cone));
9175       for (c = 0; c < coneSize; ++c) {
9176         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
9177         if (cone[c] != pp) {
9178           c = 0;
9179           break;
9180         }
9181         if (cone[c] == p) break;
9182       }
9183       if (c >= coneSize) {
9184         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
9185         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
9186         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9187         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
9188         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
9189         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9190         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
9191       }
9192     }
9193   }
9194   if (storagecheck) {
9195     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
9196     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
9197     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
9198   }
9199   PetscFunctionReturn(PETSC_SUCCESS);
9200 }
9201 
9202 /*
9203   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.
9204 */
9205 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
9206 {
9207   DMPolytopeType  cct;
9208   PetscInt        ptpoints[4];
9209   const PetscInt *cone, *ccone, *ptcone;
9210   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
9211 
9212   PetscFunctionBegin;
9213   *unsplit = 0;
9214   switch (ct) {
9215   case DM_POLYTOPE_POINT_PRISM_TENSOR:
9216     ptpoints[npt++] = c;
9217     break;
9218   case DM_POLYTOPE_SEG_PRISM_TENSOR:
9219     PetscCall(DMPlexGetCone(dm, c, &cone));
9220     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9221     for (cp = 0; cp < coneSize; ++cp) {
9222       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
9223       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
9224     }
9225     break;
9226   case DM_POLYTOPE_TRI_PRISM_TENSOR:
9227   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9228     PetscCall(DMPlexGetCone(dm, c, &cone));
9229     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9230     for (cp = 0; cp < coneSize; ++cp) {
9231       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
9232       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
9233       for (ccp = 0; ccp < cconeSize; ++ccp) {
9234         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
9235         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
9236           PetscInt p;
9237           for (p = 0; p < npt; ++p)
9238             if (ptpoints[p] == ccone[ccp]) break;
9239           if (p == npt) ptpoints[npt++] = ccone[ccp];
9240         }
9241       }
9242     }
9243     break;
9244   default:
9245     break;
9246   }
9247   for (pt = 0; pt < npt; ++pt) {
9248     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
9249     if (ptcone[0] == ptcone[1]) ++(*unsplit);
9250   }
9251   PetscFunctionReturn(PETSC_SUCCESS);
9252 }
9253 
9254 /*@
9255   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
9256 
9257   Input Parameters:
9258 + dm         - The `DMPLEX` object
9259 - cellHeight - Normally 0
9260 
9261   Level: developer
9262 
9263   Notes:
9264   This is a useful diagnostic when creating meshes programmatically.
9265   Currently applicable only to homogeneous simplex or tensor meshes.
9266 
9267   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9268 
9269 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9270 @*/
9271 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
9272 {
9273   DMPlexInterpolatedFlag interp;
9274   DMPolytopeType         ct;
9275   PetscInt               vStart, vEnd, cStart, cEnd, c;
9276 
9277   PetscFunctionBegin;
9278   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9279   PetscCall(DMPlexIsInterpolated(dm, &interp));
9280   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9281   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9282   for (c = cStart; c < cEnd; ++c) {
9283     PetscInt *closure = NULL;
9284     PetscInt  coneSize, closureSize, cl, Nv = 0;
9285 
9286     PetscCall(DMPlexGetCellType(dm, c, &ct));
9287     if (ct == DM_POLYTOPE_UNKNOWN) continue;
9288     if (interp == DMPLEX_INTERPOLATED_FULL) {
9289       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9290       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));
9291     }
9292     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9293     for (cl = 0; cl < closureSize * 2; cl += 2) {
9294       const PetscInt p = closure[cl];
9295       if ((p >= vStart) && (p < vEnd)) ++Nv;
9296     }
9297     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9298     /* Special Case: Tensor faces with identified vertices */
9299     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
9300       PetscInt unsplit;
9301 
9302       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9303       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
9304     }
9305     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));
9306   }
9307   PetscFunctionReturn(PETSC_SUCCESS);
9308 }
9309 
9310 /*@
9311   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
9312 
9313   Collective
9314 
9315   Input Parameters:
9316 + dm         - The `DMPLEX` object
9317 - cellHeight - Normally 0
9318 
9319   Level: developer
9320 
9321   Notes:
9322   This is a useful diagnostic when creating meshes programmatically.
9323   This routine is only relevant for meshes that are fully interpolated across all ranks.
9324   It will error out if a partially interpolated mesh is given on some rank.
9325   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
9326 
9327   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9328 
9329 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
9330 @*/
9331 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
9332 {
9333   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
9334   DMPlexInterpolatedFlag interpEnum;
9335 
9336   PetscFunctionBegin;
9337   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9338   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
9339   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(PETSC_SUCCESS);
9340   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
9341     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported"));
9342     PetscFunctionReturn(PETSC_SUCCESS);
9343   }
9344 
9345   PetscCall(DMGetDimension(dm, &dim));
9346   PetscCall(DMPlexGetDepth(dm, &depth));
9347   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9348   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
9349     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
9350     for (c = cStart; c < cEnd; ++c) {
9351       const PetscInt       *cone, *ornt, *faceSizes, *faces;
9352       const DMPolytopeType *faceTypes;
9353       DMPolytopeType        ct;
9354       PetscInt              numFaces, coneSize, f;
9355       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
9356 
9357       PetscCall(DMPlexGetCellType(dm, c, &ct));
9358       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9359       if (unsplit) continue;
9360       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9361       PetscCall(DMPlexGetCone(dm, c, &cone));
9362       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
9363       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9364       for (cl = 0; cl < closureSize * 2; cl += 2) {
9365         const PetscInt p = closure[cl];
9366         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
9367       }
9368       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9369       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);
9370       for (f = 0; f < numFaces; ++f) {
9371         DMPolytopeType fct;
9372         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
9373 
9374         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
9375         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
9376         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
9377           const PetscInt p = fclosure[cl];
9378           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
9379         }
9380         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]);
9381         for (v = 0; v < fnumCorners; ++v) {
9382           if (fclosure[v] != faces[fOff + v]) {
9383             PetscInt v1;
9384 
9385             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
9386             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
9387             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
9388             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
9389             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9390             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]);
9391           }
9392         }
9393         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
9394         fOff += faceSizes[f];
9395       }
9396       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9397       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9398     }
9399   }
9400   PetscFunctionReturn(PETSC_SUCCESS);
9401 }
9402 
9403 /*@
9404   DMPlexCheckGeometry - Check the geometry of mesh cells
9405 
9406   Input Parameter:
9407 . dm - The `DMPLEX` object
9408 
9409   Level: developer
9410 
9411   Notes:
9412   This is a useful diagnostic when creating meshes programmatically.
9413 
9414   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9415 
9416 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9417 @*/
9418 PetscErrorCode DMPlexCheckGeometry(DM dm)
9419 {
9420   Vec       coordinates;
9421   PetscReal detJ, J[9], refVol = 1.0;
9422   PetscReal vol;
9423   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
9424 
9425   PetscFunctionBegin;
9426   PetscCall(DMGetDimension(dm, &dim));
9427   PetscCall(DMGetCoordinateDim(dm, &dE));
9428   if (dim != dE) PetscFunctionReturn(PETSC_SUCCESS);
9429   PetscCall(DMPlexGetDepth(dm, &depth));
9430   for (d = 0; d < dim; ++d) refVol *= 2.0;
9431   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9432   /* Make sure local coordinates are created, because that step is collective */
9433   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
9434   if (!coordinates) PetscFunctionReturn(PETSC_SUCCESS);
9435   for (c = cStart; c < cEnd; ++c) {
9436     DMPolytopeType ct;
9437     PetscInt       unsplit;
9438     PetscBool      ignoreZeroVol = PETSC_FALSE;
9439 
9440     PetscCall(DMPlexGetCellType(dm, c, &ct));
9441     switch (ct) {
9442     case DM_POLYTOPE_SEG_PRISM_TENSOR:
9443     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9444     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9445       ignoreZeroVol = PETSC_TRUE;
9446       break;
9447     default:
9448       break;
9449     }
9450     switch (ct) {
9451     case DM_POLYTOPE_TRI_PRISM:
9452     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9453     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9454     case DM_POLYTOPE_PYRAMID:
9455       continue;
9456     default:
9457       break;
9458     }
9459     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9460     if (unsplit) continue;
9461     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
9462     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);
9463     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
9464     /* This should work with periodicity since DG coordinates should be used */
9465     if (depth > 1) {
9466       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
9467       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);
9468       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
9469     }
9470   }
9471   PetscFunctionReturn(PETSC_SUCCESS);
9472 }
9473 
9474 /*@
9475   DMPlexCheckPointSF - Check that several necessary conditions are met for the point `PetscSF` of this plex.
9476 
9477   Collective
9478 
9479   Input Parameters:
9480 + dm              - The `DMPLEX` object
9481 . pointSF         - The `PetscSF`, or `NULL` for `PointSF` attached to `DM`
9482 - allowExtraRoots - Flag to allow extra points not present in the `DM`
9483 
9484   Level: developer
9485 
9486   Notes:
9487   This is mainly intended for debugging/testing purposes.
9488 
9489   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9490 
9491   Extra roots can come from periodic cuts, where additional points appear on the boundary
9492 
9493 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
9494 @*/
9495 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
9496 {
9497   PetscInt           l, nleaves, nroots, overlap;
9498   const PetscInt    *locals;
9499   const PetscSFNode *remotes;
9500   PetscBool          distributed;
9501   MPI_Comm           comm;
9502   PetscMPIInt        rank;
9503 
9504   PetscFunctionBegin;
9505   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9506   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
9507   else pointSF = dm->sf;
9508   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9509   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
9510   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9511   {
9512     PetscMPIInt mpiFlag;
9513 
9514     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
9515     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
9516   }
9517   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
9518   PetscCall(DMPlexIsDistributed(dm, &distributed));
9519   if (!distributed) {
9520     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);
9521     PetscFunctionReturn(PETSC_SUCCESS);
9522   }
9523   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);
9524   PetscCall(DMPlexGetOverlap(dm, &overlap));
9525 
9526   /* Check SF graph is compatible with DMPlex chart */
9527   {
9528     PetscInt pStart, pEnd, maxLeaf;
9529 
9530     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9531     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9532     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9533     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9534   }
9535 
9536   /* Check Point SF has no local points referenced */
9537   for (l = 0; l < nleaves; l++) {
9538     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);
9539   }
9540 
9541   /* Check there are no cells in interface */
9542   if (!overlap) {
9543     PetscInt cellHeight, cStart, cEnd;
9544 
9545     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9546     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9547     for (l = 0; l < nleaves; ++l) {
9548       const PetscInt point = locals ? locals[l] : l;
9549 
9550       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9551     }
9552   }
9553 
9554   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9555   {
9556     const PetscInt *rootdegree;
9557 
9558     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9559     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9560     for (l = 0; l < nleaves; ++l) {
9561       const PetscInt  point = locals ? locals[l] : l;
9562       const PetscInt *cone;
9563       PetscInt        coneSize, c, idx;
9564 
9565       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9566       PetscCall(DMPlexGetCone(dm, point, &cone));
9567       for (c = 0; c < coneSize; ++c) {
9568         if (!rootdegree[cone[c]]) {
9569           if (locals) {
9570             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9571           } else {
9572             idx = (cone[c] < nleaves) ? cone[c] : -1;
9573           }
9574           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9575         }
9576       }
9577     }
9578   }
9579   PetscFunctionReturn(PETSC_SUCCESS);
9580 }
9581 
9582 /*@
9583   DMPlexCheckOrphanVertices - Check that no vertices are disconnected from the mesh, unless the mesh only consists of disconnected vertices.
9584 
9585   Collective
9586 
9587   Input Parameter:
9588 . dm - The `DMPLEX` object
9589 
9590   Level: developer
9591 
9592   Notes:
9593   This is mainly intended for debugging/testing purposes.
9594 
9595   Other cell types which are disconnected would be caught by the symmetry and face checks.
9596 
9597   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9598 
9599 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheck()`, `DMSetFromOptions()`
9600 @*/
9601 PetscErrorCode DMPlexCheckOrphanVertices(DM dm)
9602 {
9603   PetscInt pStart, pEnd, vStart, vEnd;
9604 
9605   PetscFunctionBegin;
9606   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9607   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9608   if (pStart == vStart && pEnd == vEnd) PetscFunctionReturn(PETSC_SUCCESS);
9609   for (PetscInt v = vStart; v < vEnd; ++v) {
9610     PetscInt suppSize;
9611 
9612     PetscCall(DMPlexGetSupportSize(dm, v, &suppSize));
9613     PetscCheck(suppSize, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Vertex %" PetscInt_FMT " is disconnected from the mesh", v);
9614   }
9615   PetscFunctionReturn(PETSC_SUCCESS);
9616 }
9617 
9618 /*@
9619   DMPlexCheck - Perform various checks of `DMPLEX` sanity
9620 
9621   Input Parameter:
9622 . dm - The `DMPLEX` object
9623 
9624   Level: developer
9625 
9626   Notes:
9627   This is a useful diagnostic when creating meshes programmatically.
9628 
9629   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9630 
9631   Currently does not include `DMPlexCheckCellShape()`.
9632 
9633 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9634 @*/
9635 PetscErrorCode DMPlexCheck(DM dm)
9636 {
9637   PetscInt cellHeight;
9638 
9639   PetscFunctionBegin;
9640   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9641   PetscCall(DMPlexCheckSymmetry(dm));
9642   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9643   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9644   PetscCall(DMPlexCheckGeometry(dm));
9645   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9646   PetscCall(DMPlexCheckInterfaceCones(dm));
9647   PetscCall(DMPlexCheckOrphanVertices(dm));
9648   PetscFunctionReturn(PETSC_SUCCESS);
9649 }
9650 
9651 typedef struct cell_stats {
9652   PetscReal min, max, sum, squaresum;
9653   PetscInt  count;
9654 } cell_stats_t;
9655 
9656 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9657 {
9658   PetscInt i, N = *len;
9659 
9660   for (i = 0; i < N; i++) {
9661     cell_stats_t *A = (cell_stats_t *)a;
9662     cell_stats_t *B = (cell_stats_t *)b;
9663 
9664     B->min = PetscMin(A->min, B->min);
9665     B->max = PetscMax(A->max, B->max);
9666     B->sum += A->sum;
9667     B->squaresum += A->squaresum;
9668     B->count += A->count;
9669   }
9670 }
9671 
9672 /*@
9673   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9674 
9675   Collective
9676 
9677   Input Parameters:
9678 + dm        - The `DMPLEX` object
9679 . output    - If true, statistics will be displayed on `stdout`
9680 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9681 
9682   Level: developer
9683 
9684   Notes:
9685   This is mainly intended for debugging/testing purposes.
9686 
9687   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9688 
9689 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9690 @*/
9691 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9692 {
9693   DM           dmCoarse;
9694   cell_stats_t stats, globalStats;
9695   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9696   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9697   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9698   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9699   PetscMPIInt  rank, size;
9700 
9701   PetscFunctionBegin;
9702   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9703   stats.min = PETSC_MAX_REAL;
9704   stats.max = PETSC_MIN_REAL;
9705   stats.sum = stats.squaresum = 0.;
9706   stats.count                 = 0;
9707 
9708   PetscCallMPI(MPI_Comm_size(comm, &size));
9709   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9710   PetscCall(DMGetCoordinateDim(dm, &cdim));
9711   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9712   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9713   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9714   for (c = cStart; c < cEnd; c++) {
9715     PetscInt  i;
9716     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9717 
9718     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9719     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9720     for (i = 0; i < PetscSqr(cdim); ++i) {
9721       frobJ += J[i] * J[i];
9722       frobInvJ += invJ[i] * invJ[i];
9723     }
9724     cond2 = frobJ * frobInvJ;
9725     cond  = PetscSqrtReal(cond2);
9726 
9727     stats.min = PetscMin(stats.min, cond);
9728     stats.max = PetscMax(stats.max, cond);
9729     stats.sum += cond;
9730     stats.squaresum += cond2;
9731     stats.count++;
9732     if (output && cond > limit) {
9733       PetscSection coordSection;
9734       Vec          coordsLocal;
9735       PetscScalar *coords = NULL;
9736       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9737 
9738       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9739       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9740       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9741       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9742       for (i = 0; i < Nv / cdim; ++i) {
9743         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9744         for (d = 0; d < cdim; ++d) {
9745           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9746           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9747         }
9748         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9749       }
9750       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9751       for (cl = 0; cl < clSize * 2; cl += 2) {
9752         const PetscInt edge = closure[cl];
9753 
9754         if ((edge >= eStart) && (edge < eEnd)) {
9755           PetscReal len;
9756 
9757           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9758           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9759         }
9760       }
9761       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9762       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9763     }
9764   }
9765   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9766 
9767   if (size > 1) {
9768     PetscMPIInt  blockLengths[2] = {4, 1};
9769     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9770     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9771     MPI_Op       statReduce;
9772 
9773     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9774     PetscCallMPI(MPI_Type_commit(&statType));
9775     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9776     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9777     PetscCallMPI(MPI_Op_free(&statReduce));
9778     PetscCallMPI(MPI_Type_free(&statType));
9779   } else {
9780     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9781   }
9782   if (rank == 0) {
9783     count = globalStats.count;
9784     min   = globalStats.min;
9785     max   = globalStats.max;
9786     mean  = globalStats.sum / globalStats.count;
9787     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9788   }
9789 
9790   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));
9791   PetscCall(PetscFree2(J, invJ));
9792 
9793   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9794   if (dmCoarse) {
9795     PetscBool isplex;
9796 
9797     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9798     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9799   }
9800   PetscFunctionReturn(PETSC_SUCCESS);
9801 }
9802 
9803 /*@
9804   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9805   orthogonal quality below given tolerance.
9806 
9807   Collective
9808 
9809   Input Parameters:
9810 + dm   - The `DMPLEX` object
9811 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9812 - atol - [0, 1] Absolute tolerance for tagging cells.
9813 
9814   Output Parameters:
9815 + OrthQual      - `Vec` containing orthogonal quality per cell
9816 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9817 
9818   Options Database Keys:
9819 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9820 - -dm_plex_orthogonal_quality_vec_view   - view OrthQual vector.
9821 
9822   Level: intermediate
9823 
9824   Notes:
9825   Orthogonal quality is given by the following formula\:
9826 
9827   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9828 
9829   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
9830   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9831   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9832   calculating the cosine of the angle between these vectors.
9833 
9834   Orthogonal quality ranges from 1 (best) to 0 (worst).
9835 
9836   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9837   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9838 
9839   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9840 
9841 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9842 @*/
9843 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9844 {
9845   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9846   PetscInt              *idx;
9847   PetscScalar           *oqVals;
9848   const PetscScalar     *cellGeomArr, *faceGeomArr;
9849   PetscReal             *ci, *fi, *Ai;
9850   MPI_Comm               comm;
9851   Vec                    cellgeom, facegeom;
9852   DM                     dmFace, dmCell;
9853   IS                     glob;
9854   ISLocalToGlobalMapping ltog;
9855   PetscViewer            vwr;
9856 
9857   PetscFunctionBegin;
9858   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9859   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9860   PetscAssertPointer(OrthQual, 4);
9861   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9862   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9863   PetscCall(DMGetDimension(dm, &nc));
9864   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9865   {
9866     DMPlexInterpolatedFlag interpFlag;
9867 
9868     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9869     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9870       PetscMPIInt rank;
9871 
9872       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9873       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9874     }
9875   }
9876   if (OrthQualLabel) {
9877     PetscAssertPointer(OrthQualLabel, 5);
9878     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9879     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9880   } else {
9881     *OrthQualLabel = NULL;
9882   }
9883   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9884   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9885   PetscCall(DMPlexCreateCellNumbering(dm, PETSC_TRUE, &glob));
9886   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9887   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9888   PetscCall(VecCreate(comm, OrthQual));
9889   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9890   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9891   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9892   PetscCall(VecSetUp(*OrthQual));
9893   PetscCall(ISDestroy(&glob));
9894   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9895   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9896   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9897   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9898   PetscCall(VecGetDM(cellgeom, &dmCell));
9899   PetscCall(VecGetDM(facegeom, &dmFace));
9900   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9901   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
9902     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9903     PetscInt         cellarr[2], *adj = NULL;
9904     PetscScalar     *cArr, *fArr;
9905     PetscReal        minvalc = 1.0, minvalf = 1.0;
9906     PetscFVCellGeom *cg;
9907 
9908     idx[cellIter] = cell - cStart;
9909     cellarr[0]    = cell;
9910     /* Make indexing into cellGeom easier */
9911     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9912     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9913     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9914     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9915     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
9916       PetscInt         i;
9917       const PetscInt   neigh  = adj[cellneigh];
9918       PetscReal        normci = 0, normfi = 0, normai = 0;
9919       PetscFVCellGeom *cgneigh;
9920       PetscFVFaceGeom *fg;
9921 
9922       /* Don't count ourselves in the neighbor list */
9923       if (neigh == cell) continue;
9924       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9925       cellarr[1] = neigh;
9926       {
9927         PetscInt        numcovpts;
9928         const PetscInt *covpts;
9929 
9930         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9931         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9932         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9933       }
9934 
9935       /* Compute c_i, f_i and their norms */
9936       for (i = 0; i < nc; i++) {
9937         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9938         fi[i] = fg->centroid[i] - cg->centroid[i];
9939         Ai[i] = fg->normal[i];
9940         normci += PetscPowReal(ci[i], 2);
9941         normfi += PetscPowReal(fi[i], 2);
9942         normai += PetscPowReal(Ai[i], 2);
9943       }
9944       normci = PetscSqrtReal(normci);
9945       normfi = PetscSqrtReal(normfi);
9946       normai = PetscSqrtReal(normai);
9947 
9948       /* Normalize and compute for each face-cell-normal pair */
9949       for (i = 0; i < nc; i++) {
9950         ci[i] = ci[i] / normci;
9951         fi[i] = fi[i] / normfi;
9952         Ai[i] = Ai[i] / normai;
9953         /* PetscAbs because I don't know if normals are guaranteed to point out */
9954         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
9955         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
9956       }
9957       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
9958       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
9959     }
9960     PetscCall(PetscFree(adj));
9961     PetscCall(PetscFree2(cArr, fArr));
9962     /* Defer to cell if they're equal */
9963     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9964     if (OrthQualLabel) {
9965       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9966     }
9967   }
9968   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
9969   PetscCall(VecAssemblyBegin(*OrthQual));
9970   PetscCall(VecAssemblyEnd(*OrthQual));
9971   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9972   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9973   PetscCall(PetscOptionsCreateViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9974   if (OrthQualLabel) {
9975     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9976   }
9977   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9978   PetscCall(PetscViewerDestroy(&vwr));
9979   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9980   PetscFunctionReturn(PETSC_SUCCESS);
9981 }
9982 
9983 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9984  * interpolator construction */
9985 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9986 {
9987   PetscSection section, newSection, gsection;
9988   PetscSF      sf;
9989   PetscBool    hasConstraints, ghasConstraints;
9990 
9991   PetscFunctionBegin;
9992   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9993   PetscAssertPointer(odm, 2);
9994   PetscCall(DMGetLocalSection(dm, &section));
9995   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9996   PetscCall(MPIU_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
9997   if (!ghasConstraints) {
9998     PetscCall(PetscObjectReference((PetscObject)dm));
9999     *odm = dm;
10000     PetscFunctionReturn(PETSC_SUCCESS);
10001   }
10002   PetscCall(DMClone(dm, odm));
10003   PetscCall(DMCopyFields(dm, *odm));
10004   PetscCall(DMGetLocalSection(*odm, &newSection));
10005   PetscCall(DMGetPointSF(*odm, &sf));
10006   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_TRUE, PETSC_FALSE, &gsection));
10007   PetscCall(DMSetGlobalSection(*odm, gsection));
10008   PetscCall(PetscSectionDestroy(&gsection));
10009   PetscFunctionReturn(PETSC_SUCCESS);
10010 }
10011 
10012 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
10013 {
10014   DM        dmco, dmfo;
10015   Mat       interpo;
10016   Vec       rscale;
10017   Vec       cglobalo, clocal;
10018   Vec       fglobal, fglobalo, flocal;
10019   PetscBool regular;
10020 
10021   PetscFunctionBegin;
10022   PetscCall(DMGetFullDM(dmc, &dmco));
10023   PetscCall(DMGetFullDM(dmf, &dmfo));
10024   PetscCall(DMSetCoarseDM(dmfo, dmco));
10025   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
10026   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
10027   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
10028   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
10029   PetscCall(DMCreateLocalVector(dmc, &clocal));
10030   PetscCall(VecSet(cglobalo, 0.));
10031   PetscCall(VecSet(clocal, 0.));
10032   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
10033   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
10034   PetscCall(DMCreateLocalVector(dmf, &flocal));
10035   PetscCall(VecSet(fglobal, 0.));
10036   PetscCall(VecSet(fglobalo, 0.));
10037   PetscCall(VecSet(flocal, 0.));
10038   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
10039   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
10040   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
10041   PetscCall(MatMult(interpo, cglobalo, fglobalo));
10042   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
10043   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
10044   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
10045   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
10046   *shift = fglobal;
10047   PetscCall(VecDestroy(&flocal));
10048   PetscCall(VecDestroy(&fglobalo));
10049   PetscCall(VecDestroy(&clocal));
10050   PetscCall(VecDestroy(&cglobalo));
10051   PetscCall(VecDestroy(&rscale));
10052   PetscCall(MatDestroy(&interpo));
10053   PetscCall(DMDestroy(&dmfo));
10054   PetscCall(DMDestroy(&dmco));
10055   PetscFunctionReturn(PETSC_SUCCESS);
10056 }
10057 
10058 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
10059 {
10060   PetscObject shifto;
10061   Vec         shift;
10062 
10063   PetscFunctionBegin;
10064   if (!interp) {
10065     Vec rscale;
10066 
10067     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
10068     PetscCall(VecDestroy(&rscale));
10069   } else {
10070     PetscCall(PetscObjectReference((PetscObject)interp));
10071   }
10072   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
10073   if (!shifto) {
10074     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
10075     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
10076     shifto = (PetscObject)shift;
10077     PetscCall(VecDestroy(&shift));
10078   }
10079   shift = (Vec)shifto;
10080   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
10081   PetscCall(VecAXPY(fineSol, 1.0, shift));
10082   PetscCall(MatDestroy(&interp));
10083   PetscFunctionReturn(PETSC_SUCCESS);
10084 }
10085 
10086 /* Pointwise interpolation
10087      Just code FEM for now
10088      u^f = I u^c
10089      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
10090      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
10091      I_{ij} = psi^f_i phi^c_j
10092 */
10093 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
10094 {
10095   PetscSection gsc, gsf;
10096   PetscInt     m, n;
10097   void        *ctx;
10098   DM           cdm;
10099   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
10100 
10101   PetscFunctionBegin;
10102   PetscCall(DMGetGlobalSection(dmFine, &gsf));
10103   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10104   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10105   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10106 
10107   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
10108   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
10109   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10110   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
10111   PetscCall(DMGetApplicationContext(dmFine, &ctx));
10112 
10113   PetscCall(DMGetCoarseDM(dmFine, &cdm));
10114   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10115   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
10116   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
10117   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
10118   if (scaling) {
10119     /* Use naive scaling */
10120     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
10121   }
10122   PetscFunctionReturn(PETSC_SUCCESS);
10123 }
10124 
10125 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
10126 {
10127   VecScatter ctx;
10128 
10129   PetscFunctionBegin;
10130   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
10131   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
10132   PetscCall(VecScatterDestroy(&ctx));
10133   PetscFunctionReturn(PETSC_SUCCESS);
10134 }
10135 
10136 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[])
10137 {
10138   const PetscInt f  = (PetscInt)PetscRealPart(constants[numConstants]);
10139   const PetscInt Nc = uOff[f + 1] - uOff[f];
10140   for (PetscInt c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
10141 }
10142 
10143 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *lmass, Vec *mass)
10144 {
10145   DM           dmc;
10146   PetscDS      ds;
10147   Vec          ones, locmass;
10148   IS           cellIS;
10149   PetscFormKey key;
10150   PetscInt     depth;
10151 
10152   PetscFunctionBegin;
10153   PetscCall(DMClone(dm, &dmc));
10154   PetscCall(DMCopyDisc(dm, dmc));
10155   PetscCall(DMGetDS(dmc, &ds));
10156   for (PetscInt f = 0; f < dmc->Nf; ++f) PetscCall(PetscDSSetJacobian(ds, f, f, g0_identity_private, NULL, NULL, NULL));
10157   if (mass) PetscCall(DMCreateGlobalVector(dm, mass));
10158   if (lmass) PetscCall(DMCreateLocalVector(dm, &locmass));
10159   else PetscCall(DMGetLocalVector(dm, &locmass));
10160   PetscCall(DMGetLocalVector(dm, &ones));
10161   PetscCall(DMPlexGetDepth(dm, &depth));
10162   PetscCall(DMGetStratumIS(dm, "depth", depth, &cellIS));
10163   PetscCall(VecSet(locmass, 0.0));
10164   PetscCall(VecSet(ones, 1.0));
10165   key.label = NULL;
10166   key.value = 0;
10167   key.field = 0;
10168   key.part  = 0;
10169   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
10170   PetscCall(ISDestroy(&cellIS));
10171   if (mass) {
10172     PetscCall(DMLocalToGlobalBegin(dm, locmass, ADD_VALUES, *mass));
10173     PetscCall(DMLocalToGlobalEnd(dm, locmass, ADD_VALUES, *mass));
10174   }
10175   PetscCall(DMRestoreLocalVector(dm, &ones));
10176   if (lmass) *lmass = locmass;
10177   else PetscCall(DMRestoreLocalVector(dm, &locmass));
10178   PetscCall(DMDestroy(&dmc));
10179   PetscFunctionReturn(PETSC_SUCCESS);
10180 }
10181 
10182 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
10183 {
10184   PetscSection gsc, gsf;
10185   PetscInt     m, n;
10186   void        *ctx;
10187   DM           cdm;
10188   PetscBool    regular;
10189 
10190   PetscFunctionBegin;
10191   if (dmFine == dmCoarse) {
10192     DM            dmc;
10193     PetscDS       ds;
10194     PetscWeakForm wf;
10195     Vec           u;
10196     IS            cellIS;
10197     PetscFormKey  key;
10198     PetscInt      depth;
10199 
10200     PetscCall(DMClone(dmFine, &dmc));
10201     PetscCall(DMCopyDisc(dmFine, dmc));
10202     PetscCall(DMGetDS(dmc, &ds));
10203     PetscCall(PetscDSGetWeakForm(ds, &wf));
10204     PetscCall(PetscWeakFormClear(wf));
10205     for (PetscInt f = 0; f < dmc->Nf; ++f) PetscCall(PetscDSSetJacobian(ds, f, f, g0_identity_private, NULL, NULL, NULL));
10206     PetscCall(DMCreateMatrix(dmc, mass));
10207     PetscCall(DMGetLocalVector(dmc, &u));
10208     PetscCall(DMPlexGetDepth(dmc, &depth));
10209     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
10210     PetscCall(MatZeroEntries(*mass));
10211     key.label = NULL;
10212     key.value = 0;
10213     key.field = 0;
10214     key.part  = 0;
10215     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
10216     PetscCall(ISDestroy(&cellIS));
10217     PetscCall(DMRestoreLocalVector(dmc, &u));
10218     PetscCall(DMDestroy(&dmc));
10219   } else {
10220     PetscCall(DMGetGlobalSection(dmFine, &gsf));
10221     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10222     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10223     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10224 
10225     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
10226     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10227     PetscCall(MatSetType(*mass, dmCoarse->mattype));
10228     PetscCall(DMGetApplicationContext(dmFine, &ctx));
10229 
10230     PetscCall(DMGetCoarseDM(dmFine, &cdm));
10231     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10232     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
10233     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
10234   }
10235   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
10236   PetscFunctionReturn(PETSC_SUCCESS);
10237 }
10238 
10239 /*@
10240   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10241 
10242   Input Parameter:
10243 . dm - The `DMPLEX` object
10244 
10245   Output Parameter:
10246 . regular - The flag
10247 
10248   Level: intermediate
10249 
10250 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
10251 @*/
10252 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
10253 {
10254   PetscFunctionBegin;
10255   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10256   PetscAssertPointer(regular, 2);
10257   *regular = ((DM_Plex *)dm->data)->regularRefinement;
10258   PetscFunctionReturn(PETSC_SUCCESS);
10259 }
10260 
10261 /*@
10262   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10263 
10264   Input Parameters:
10265 + dm      - The `DMPLEX` object
10266 - regular - The flag
10267 
10268   Level: intermediate
10269 
10270 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
10271 @*/
10272 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
10273 {
10274   PetscFunctionBegin;
10275   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10276   ((DM_Plex *)dm->data)->regularRefinement = regular;
10277   PetscFunctionReturn(PETSC_SUCCESS);
10278 }
10279 
10280 /*@
10281   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
10282   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
10283 
10284   Not Collective
10285 
10286   Input Parameter:
10287 . dm - The `DMPLEX` object
10288 
10289   Output Parameters:
10290 + anchorSection - If not `NULL`, set to the section describing which points anchor the constrained points.
10291 - anchorIS      - If not `NULL`, set to the list of anchors indexed by `anchorSection`
10292 
10293   Level: intermediate
10294 
10295 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
10296 @*/
10297 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
10298 {
10299   DM_Plex *plex = (DM_Plex *)dm->data;
10300 
10301   PetscFunctionBegin;
10302   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10303   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
10304   if (anchorSection) *anchorSection = plex->anchorSection;
10305   if (anchorIS) *anchorIS = plex->anchorIS;
10306   PetscFunctionReturn(PETSC_SUCCESS);
10307 }
10308 
10309 /*@
10310   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.
10311 
10312   Collective
10313 
10314   Input Parameters:
10315 + dm            - The `DMPLEX` object
10316 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
10317                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10318 - anchorIS      - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10319 
10320   Level: intermediate
10321 
10322   Notes:
10323   Unlike boundary conditions, when a point's degrees of freedom in a section are constrained to
10324   an outside value, the anchor constraints set a point's degrees of freedom to be a linear
10325   combination of other points' degrees of freedom.
10326 
10327   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
10328   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
10329 
10330   The reference counts of `anchorSection` and `anchorIS` are incremented.
10331 
10332 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
10333 @*/
10334 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
10335 {
10336   DM_Plex    *plex = (DM_Plex *)dm->data;
10337   PetscMPIInt result;
10338 
10339   PetscFunctionBegin;
10340   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10341   if (anchorSection) {
10342     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
10343     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
10344     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
10345   }
10346   if (anchorIS) {
10347     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
10348     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
10349     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
10350   }
10351 
10352   PetscCall(PetscObjectReference((PetscObject)anchorSection));
10353   PetscCall(PetscSectionDestroy(&plex->anchorSection));
10354   plex->anchorSection = anchorSection;
10355 
10356   PetscCall(PetscObjectReference((PetscObject)anchorIS));
10357   PetscCall(ISDestroy(&plex->anchorIS));
10358   plex->anchorIS = anchorIS;
10359 
10360   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
10361     PetscInt        size, a, pStart, pEnd;
10362     const PetscInt *anchors;
10363 
10364     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10365     PetscCall(ISGetLocalSize(anchorIS, &size));
10366     PetscCall(ISGetIndices(anchorIS, &anchors));
10367     for (a = 0; a < size; a++) {
10368       PetscInt p;
10369 
10370       p = anchors[a];
10371       if (p >= pStart && p < pEnd) {
10372         PetscInt dof;
10373 
10374         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10375         if (dof) {
10376           PetscCall(ISRestoreIndices(anchorIS, &anchors));
10377           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
10378         }
10379       }
10380     }
10381     PetscCall(ISRestoreIndices(anchorIS, &anchors));
10382   }
10383   /* reset the generic constraints */
10384   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
10385   PetscFunctionReturn(PETSC_SUCCESS);
10386 }
10387 
10388 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
10389 {
10390   PetscSection anchorSection;
10391   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
10392 
10393   PetscFunctionBegin;
10394   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10395   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10396   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
10397   PetscCall(PetscSectionGetNumFields(section, &numFields));
10398   if (numFields) {
10399     PetscInt f;
10400     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
10401 
10402     for (f = 0; f < numFields; f++) {
10403       PetscInt numComp;
10404 
10405       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
10406       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
10407     }
10408   }
10409   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10410   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10411   pStart = PetscMax(pStart, sStart);
10412   pEnd   = PetscMin(pEnd, sEnd);
10413   pEnd   = PetscMax(pStart, pEnd);
10414   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
10415   for (p = pStart; p < pEnd; p++) {
10416     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10417     if (dof) {
10418       PetscCall(PetscSectionGetDof(section, p, &dof));
10419       PetscCall(PetscSectionSetDof(*cSec, p, dof));
10420       for (f = 0; f < numFields; f++) {
10421         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
10422         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
10423       }
10424     }
10425   }
10426   PetscCall(PetscSectionSetUp(*cSec));
10427   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
10428   PetscFunctionReturn(PETSC_SUCCESS);
10429 }
10430 
10431 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
10432 {
10433   PetscSection    aSec;
10434   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
10435   const PetscInt *anchors;
10436   PetscInt        numFields, f;
10437   IS              aIS;
10438   MatType         mtype;
10439   PetscBool       iscuda, iskokkos;
10440 
10441   PetscFunctionBegin;
10442   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10443   PetscCall(PetscSectionGetStorageSize(cSec, &m));
10444   PetscCall(PetscSectionGetStorageSize(section, &n));
10445   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
10446   PetscCall(MatSetSizes(*cMat, m, n, m, n));
10447   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
10448   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
10449   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
10450   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
10451   if (iscuda) mtype = MATSEQAIJCUSPARSE;
10452   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
10453   else mtype = MATSEQAIJ;
10454   PetscCall(MatSetType(*cMat, mtype));
10455   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
10456   PetscCall(ISGetIndices(aIS, &anchors));
10457   /* cSec will be a subset of aSec and section */
10458   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
10459   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10460   PetscCall(PetscMalloc1(m + 1, &i));
10461   i[0] = 0;
10462   PetscCall(PetscSectionGetNumFields(section, &numFields));
10463   for (p = pStart; p < pEnd; p++) {
10464     PetscInt rDof, rOff, r;
10465 
10466     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10467     if (!rDof) continue;
10468     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10469     if (numFields) {
10470       for (f = 0; f < numFields; f++) {
10471         annz = 0;
10472         for (r = 0; r < rDof; r++) {
10473           a = anchors[rOff + r];
10474           if (a < sStart || a >= sEnd) continue;
10475           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10476           annz += aDof;
10477         }
10478         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10479         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
10480         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10481       }
10482     } else {
10483       annz = 0;
10484       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10485       for (q = 0; q < dof; q++) {
10486         a = anchors[rOff + q];
10487         if (a < sStart || a >= sEnd) continue;
10488         PetscCall(PetscSectionGetDof(section, a, &aDof));
10489         annz += aDof;
10490       }
10491       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10492       PetscCall(PetscSectionGetOffset(cSec, p, &off));
10493       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10494     }
10495   }
10496   nnz = i[m];
10497   PetscCall(PetscMalloc1(nnz, &j));
10498   offset = 0;
10499   for (p = pStart; p < pEnd; p++) {
10500     if (numFields) {
10501       for (f = 0; f < numFields; f++) {
10502         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10503         for (q = 0; q < dof; q++) {
10504           PetscInt rDof, rOff, r;
10505           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10506           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10507           for (r = 0; r < rDof; r++) {
10508             PetscInt s;
10509 
10510             a = anchors[rOff + r];
10511             if (a < sStart || a >= sEnd) continue;
10512             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10513             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
10514             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10515           }
10516         }
10517       }
10518     } else {
10519       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10520       for (q = 0; q < dof; q++) {
10521         PetscInt rDof, rOff, r;
10522         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10523         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10524         for (r = 0; r < rDof; r++) {
10525           PetscInt s;
10526 
10527           a = anchors[rOff + r];
10528           if (a < sStart || a >= sEnd) continue;
10529           PetscCall(PetscSectionGetDof(section, a, &aDof));
10530           PetscCall(PetscSectionGetOffset(section, a, &aOff));
10531           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10532         }
10533       }
10534     }
10535   }
10536   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
10537   PetscCall(PetscFree(i));
10538   PetscCall(PetscFree(j));
10539   PetscCall(ISRestoreIndices(aIS, &anchors));
10540   PetscFunctionReturn(PETSC_SUCCESS);
10541 }
10542 
10543 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
10544 {
10545   DM_Plex     *plex = (DM_Plex *)dm->data;
10546   PetscSection anchorSection, section, cSec;
10547   Mat          cMat;
10548 
10549   PetscFunctionBegin;
10550   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10551   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10552   if (anchorSection) {
10553     PetscInt Nf;
10554 
10555     PetscCall(DMGetLocalSection(dm, &section));
10556     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
10557     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
10558     PetscCall(DMGetNumFields(dm, &Nf));
10559     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
10560     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
10561     PetscCall(PetscSectionDestroy(&cSec));
10562     PetscCall(MatDestroy(&cMat));
10563   }
10564   PetscFunctionReturn(PETSC_SUCCESS);
10565 }
10566 
10567 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10568 {
10569   IS           subis;
10570   PetscSection section, subsection;
10571 
10572   PetscFunctionBegin;
10573   PetscCall(DMGetLocalSection(dm, &section));
10574   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10575   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10576   /* Create subdomain */
10577   PetscCall(DMPlexFilter(dm, label, value, PETSC_FALSE, PETSC_FALSE, NULL, subdm));
10578   /* Create submodel */
10579   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10580   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10581   PetscCall(DMSetLocalSection(*subdm, subsection));
10582   PetscCall(PetscSectionDestroy(&subsection));
10583   PetscCall(DMCopyDisc(dm, *subdm));
10584   /* Create map from submodel to global model */
10585   if (is) {
10586     PetscSection    sectionGlobal, subsectionGlobal;
10587     IS              spIS;
10588     const PetscInt *spmap;
10589     PetscInt       *subIndices;
10590     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10591     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10592 
10593     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10594     PetscCall(ISGetIndices(spIS, &spmap));
10595     PetscCall(PetscSectionGetNumFields(section, &Nf));
10596     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10597     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10598     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10599     for (p = pStart; p < pEnd; ++p) {
10600       PetscInt gdof, pSubSize = 0;
10601 
10602       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10603       if (gdof > 0) {
10604         for (f = 0; f < Nf; ++f) {
10605           PetscInt fdof, fcdof;
10606 
10607           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10608           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10609           pSubSize += fdof - fcdof;
10610         }
10611         subSize += pSubSize;
10612         if (pSubSize) {
10613           if (bs < 0) {
10614             bs = pSubSize;
10615           } else if (bs != pSubSize) {
10616             /* Layout does not admit a pointwise block size */
10617             bs = 1;
10618           }
10619         }
10620       }
10621     }
10622     /* Must have same blocksize on all procs (some might have no points) */
10623     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
10624     bsLocal[1] = bs;
10625     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10626     if (bsMinMax[0] != bsMinMax[1]) {
10627       bs = 1;
10628     } else {
10629       bs = bsMinMax[0];
10630     }
10631     PetscCall(PetscMalloc1(subSize, &subIndices));
10632     for (p = pStart; p < pEnd; ++p) {
10633       PetscInt gdof, goff;
10634 
10635       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10636       if (gdof > 0) {
10637         const PetscInt point = spmap[p];
10638 
10639         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10640         for (f = 0; f < Nf; ++f) {
10641           PetscInt fdof, fcdof, fc, f2, poff = 0;
10642 
10643           /* Can get rid of this loop by storing field information in the global section */
10644           for (f2 = 0; f2 < f; ++f2) {
10645             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10646             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10647             poff += fdof - fcdof;
10648           }
10649           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10650           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10651           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10652         }
10653       }
10654     }
10655     PetscCall(ISRestoreIndices(spIS, &spmap));
10656     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10657     if (bs > 1) {
10658       /* We need to check that the block size does not come from non-contiguous fields */
10659       PetscInt i, j, set = 1;
10660       for (i = 0; i < subSize; i += bs) {
10661         for (j = 0; j < bs; ++j) {
10662           if (subIndices[i + j] != subIndices[i] + j) {
10663             set = 0;
10664             break;
10665           }
10666         }
10667       }
10668       if (set) PetscCall(ISSetBlockSize(*is, bs));
10669     }
10670     /* Attach nullspace */
10671     for (f = 0; f < Nf; ++f) {
10672       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10673       if ((*subdm)->nullspaceConstructors[f]) break;
10674     }
10675     if (f < Nf) {
10676       MatNullSpace nullSpace;
10677       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10678 
10679       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10680       PetscCall(MatNullSpaceDestroy(&nullSpace));
10681     }
10682   }
10683   PetscFunctionReturn(PETSC_SUCCESS);
10684 }
10685 
10686 /*@
10687   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10688 
10689   Input Parameters:
10690 + dm    - The `DM`
10691 - dummy - unused argument
10692 
10693   Options Database Key:
10694 . -dm_plex_monitor_throughput - Activate the monitor
10695 
10696   Level: developer
10697 
10698 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10699 @*/
10700 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10701 {
10702   PetscLogHandler default_handler;
10703 
10704   PetscFunctionBegin;
10705   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10706   PetscCall(PetscLogGetDefaultHandler(&default_handler));
10707   if (default_handler) {
10708     PetscLogEvent      event;
10709     PetscEventPerfInfo eventInfo;
10710     PetscReal          cellRate, flopRate;
10711     PetscInt           cStart, cEnd, Nf, N;
10712     const char        *name;
10713 
10714     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10715     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10716     PetscCall(DMGetNumFields(dm, &Nf));
10717     PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10718     PetscCall(PetscLogEventGetPerfInfo(PETSC_DEFAULT, event, &eventInfo));
10719     N        = (cEnd - cStart) * Nf * eventInfo.count;
10720     flopRate = eventInfo.flops / eventInfo.time;
10721     cellRate = N / eventInfo.time;
10722     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)));
10723   } else {
10724     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.");
10725   }
10726   PetscFunctionReturn(PETSC_SUCCESS);
10727 }
10728