xref: /petsc/src/dm/impls/plex/plex.c (revision 688c8ee7bc20d3b60efe08031be4f1f63ae1e457)
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_INT_MAX, 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   PetscCallMPI(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   PetscCallMPI(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;
434         int                color[4] = {-1, -1, -1, -1};
435 
436         PetscCall(DMPlexGetCellType(dm, c, &ct));
437         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
438         if (a) {
439           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
440           color[1] = color[2] = color[3] = color[0];
441         } else {
442           PetscScalar *vals = NULL;
443           PetscInt     numVals, va;
444 
445           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
446           if (!numVals) {
447             PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
448             continue;
449           }
450           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);
451           switch (numVals / Nc) {
452           case 1: /* P1 Clamped Segment Prism */
453           case 2: /* P1 Segment Prism, P2 Clamped Segment Prism */
454             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]);
455             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
456             break;
457           case 3: /* P1 Triangle */
458           case 4: /* P1 Quadrangle */
459             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]);
460             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
461             break;
462           case 6: /* P2 Triangle */
463           case 8: /* P2 Quadrangle */
464             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]);
465             for (va = 0; va < numVals / (Nc * 2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp + numVals / (Nc * 2)]), vbound[0], vbound[1]);
466             break;
467           default:
468             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals / Nc);
469           }
470           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
471         }
472         PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
473         switch (numCoords) {
474         case 6:
475         case 12: /* Localized triangle */
476           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]));
477           break;
478         case 8:
479         case 16: /* Localized quadrilateral */
480           if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR) {
481             PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscMax(color[0], color[1])));
482           } else {
483             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]));
484             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]));
485           }
486           break;
487         default:
488           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
489         }
490         PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
491       }
492       PetscCall(VecRestoreArrayRead(fv, &array));
493       PetscCall(PetscDrawFlush(draw));
494       PetscCall(PetscDrawPause(draw));
495       PetscCall(PetscDrawSave(draw));
496     }
497     if (Nf > 1) {
498       PetscCall(VecRestoreSubVector(v, fis, &fv));
499       PetscCall(ISDestroy(&fis));
500       PetscCall(DMDestroy(&fdm));
501     }
502   }
503   PetscFunctionReturn(PETSC_SUCCESS);
504 }
505 
506 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
507 {
508   DM        dm;
509   PetscDraw draw;
510   PetscInt  dim;
511   PetscBool isnull;
512 
513   PetscFunctionBegin;
514   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
515   PetscCall(PetscDrawIsNull(draw, &isnull));
516   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
517 
518   PetscCall(VecGetDM(v, &dm));
519   PetscCall(DMGetCoordinateDim(dm, &dim));
520   switch (dim) {
521   case 1:
522     PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));
523     break;
524   case 2:
525     PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));
526     break;
527   default:
528     SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
529   }
530   PetscFunctionReturn(PETSC_SUCCESS);
531 }
532 
533 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
534 {
535   DM                      dm;
536   Vec                     locv;
537   const char             *name;
538   PetscSection            section;
539   PetscInt                pStart, pEnd;
540   PetscInt                numFields;
541   PetscViewerVTKFieldType ft;
542 
543   PetscFunctionBegin;
544   PetscCall(VecGetDM(v, &dm));
545   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
546   PetscCall(PetscObjectGetName((PetscObject)v, &name));
547   PetscCall(PetscObjectSetName((PetscObject)locv, name));
548   PetscCall(VecCopy(v, locv));
549   PetscCall(DMGetLocalSection(dm, &section));
550   PetscCall(PetscSectionGetNumFields(section, &numFields));
551   if (!numFields) {
552     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
553     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE, (PetscObject)locv));
554   } else {
555     PetscInt f;
556 
557     for (f = 0; f < numFields; f++) {
558       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
559       if (ft == PETSC_VTK_INVALID) continue;
560       PetscCall(PetscObjectReference((PetscObject)locv));
561       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE, (PetscObject)locv));
562     }
563     PetscCall(VecDestroy(&locv));
564   }
565   PetscFunctionReturn(PETSC_SUCCESS);
566 }
567 
568 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
569 {
570   DM        dm;
571   PetscBool isvtk, ishdf5, isdraw, isglvis, iscgns;
572 
573   PetscFunctionBegin;
574   PetscCall(VecGetDM(v, &dm));
575   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
576   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
577   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
578   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
579   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
580   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
581   if (isvtk || ishdf5 || isdraw || isglvis || iscgns) {
582     PetscInt    i, numFields;
583     PetscObject fe;
584     PetscBool   fem  = PETSC_FALSE;
585     Vec         locv = v;
586     const char *name;
587     PetscInt    step;
588     PetscReal   time;
589 
590     PetscCall(DMGetNumFields(dm, &numFields));
591     for (i = 0; i < numFields; i++) {
592       PetscCall(DMGetField(dm, i, NULL, &fe));
593       if (fe->classid == PETSCFE_CLASSID) {
594         fem = PETSC_TRUE;
595         break;
596       }
597     }
598     if (fem) {
599       PetscObject isZero;
600 
601       PetscCall(DMGetLocalVector(dm, &locv));
602       PetscCall(PetscObjectGetName((PetscObject)v, &name));
603       PetscCall(PetscObjectSetName((PetscObject)locv, name));
604       PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
605       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
606       PetscCall(VecCopy(v, locv));
607       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
608       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
609     }
610     if (isvtk) {
611       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
612     } else if (ishdf5) {
613 #if defined(PETSC_HAVE_HDF5)
614       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
615 #else
616       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
617 #endif
618     } else if (isdraw) {
619       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
620     } else if (isglvis) {
621       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
622       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
623       PetscCall(VecView_GLVis(locv, viewer));
624     } else if (iscgns) {
625 #if defined(PETSC_HAVE_CGNS)
626       PetscCall(VecView_Plex_Local_CGNS(locv, viewer));
627 #else
628       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
629 #endif
630     }
631     if (fem) {
632       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
633       PetscCall(DMRestoreLocalVector(dm, &locv));
634     }
635   } else {
636     PetscBool isseq;
637 
638     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
639     if (isseq) PetscCall(VecView_Seq(v, viewer));
640     else PetscCall(VecView_MPI(v, viewer));
641   }
642   PetscFunctionReturn(PETSC_SUCCESS);
643 }
644 
645 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
646 {
647   DM        dm;
648   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii, iscgns;
649 
650   PetscFunctionBegin;
651   PetscCall(VecGetDM(v, &dm));
652   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
653   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
654   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
655   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
656   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
657   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
658   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
659   if (isvtk || isdraw || isglvis || iscgns) {
660     Vec         locv;
661     PetscObject isZero;
662     const char *name;
663 
664     PetscCall(DMGetLocalVector(dm, &locv));
665     PetscCall(PetscObjectGetName((PetscObject)v, &name));
666     PetscCall(PetscObjectSetName((PetscObject)locv, name));
667     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
668     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
669     PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
670     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
671     PetscCall(VecView_Plex_Local(locv, viewer));
672     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
673     PetscCall(DMRestoreLocalVector(dm, &locv));
674     /* Call flush for proper logging of VecView timings */
675     if (isvtk) PetscCall(PetscViewerFlush(viewer));
676   } else if (ishdf5) {
677 #if defined(PETSC_HAVE_HDF5)
678     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
679 #else
680     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
681 #endif
682   } else if (isexodusii) {
683 #if defined(PETSC_HAVE_EXODUSII)
684     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
685 #else
686     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
687 #endif
688   } else {
689     PetscBool isseq;
690 
691     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
692     if (isseq) PetscCall(VecView_Seq(v, viewer));
693     else PetscCall(VecView_MPI(v, viewer));
694   }
695   PetscFunctionReturn(PETSC_SUCCESS);
696 }
697 
698 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
699 {
700   DM                dm;
701   MPI_Comm          comm;
702   PetscViewerFormat format;
703   Vec               v;
704   PetscBool         isvtk, ishdf5;
705 
706   PetscFunctionBegin;
707   PetscCall(VecGetDM(originalv, &dm));
708   PetscCall(PetscObjectGetComm((PetscObject)originalv, &comm));
709   PetscCheck(dm, comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
710   PetscCall(PetscViewerGetFormat(viewer, &format));
711   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
712   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
713   if (format == PETSC_VIEWER_NATIVE) {
714     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
715     /* this need a better fix */
716     if (dm->useNatural) {
717       if (dm->sfNatural) {
718         const char *vecname;
719         PetscInt    n, nroots;
720 
721         PetscCall(VecGetLocalSize(originalv, &n));
722         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
723         if (n == nroots) {
724           PetscCall(DMPlexCreateNaturalVector(dm, &v));
725           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
726           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
727           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
728           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
729         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
730       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
731     } else v = originalv;
732   } else v = originalv;
733 
734   if (ishdf5) {
735 #if defined(PETSC_HAVE_HDF5)
736     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
737 #else
738     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
739 #endif
740   } else if (isvtk) {
741     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
742   } else {
743     PetscBool isseq;
744 
745     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
746     if (isseq) PetscCall(VecView_Seq(v, viewer));
747     else PetscCall(VecView_MPI(v, viewer));
748   }
749   if (v != originalv) PetscCall(VecDestroy(&v));
750   PetscFunctionReturn(PETSC_SUCCESS);
751 }
752 
753 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
754 {
755   DM        dm;
756   PetscBool ishdf5;
757 
758   PetscFunctionBegin;
759   PetscCall(VecGetDM(v, &dm));
760   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
761   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
762   if (ishdf5) {
763     DM          dmBC;
764     Vec         gv;
765     const char *name;
766 
767     PetscCall(DMGetOutputDM(dm, &dmBC));
768     PetscCall(DMGetGlobalVector(dmBC, &gv));
769     PetscCall(PetscObjectGetName((PetscObject)v, &name));
770     PetscCall(PetscObjectSetName((PetscObject)gv, name));
771     PetscCall(VecLoad_Default(gv, viewer));
772     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
773     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
774     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
775   } else PetscCall(VecLoad_Default(v, viewer));
776   PetscFunctionReturn(PETSC_SUCCESS);
777 }
778 
779 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
780 {
781   DM        dm;
782   PetscBool ishdf5, isexodusii, iscgns;
783 
784   PetscFunctionBegin;
785   PetscCall(VecGetDM(v, &dm));
786   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
787   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
788   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
789   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
790   if (ishdf5) {
791 #if defined(PETSC_HAVE_HDF5)
792     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
793 #else
794     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
795 #endif
796   } else if (isexodusii) {
797 #if defined(PETSC_HAVE_EXODUSII)
798     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
799 #else
800     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
801 #endif
802   } else if (iscgns) {
803 #if defined(PETSC_HAVE_CGNS)
804     PetscCall(VecLoad_Plex_CGNS_Internal(v, viewer));
805 #else
806     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
807 #endif
808   } else PetscCall(VecLoad_Default(v, viewer));
809   PetscFunctionReturn(PETSC_SUCCESS);
810 }
811 
812 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
813 {
814   DM                dm;
815   PetscViewerFormat format;
816   PetscBool         ishdf5;
817 
818   PetscFunctionBegin;
819   PetscCall(VecGetDM(originalv, &dm));
820   PetscCheck(dm, PetscObjectComm((PetscObject)originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
821   PetscCall(PetscViewerGetFormat(viewer, &format));
822   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
823   if (format == PETSC_VIEWER_NATIVE) {
824     if (dm->useNatural) {
825       if (dm->sfNatural) {
826         if (ishdf5) {
827 #if defined(PETSC_HAVE_HDF5)
828           Vec         v;
829           const char *vecname;
830 
831           PetscCall(DMPlexCreateNaturalVector(dm, &v));
832           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
833           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
834           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
835           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
836           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
837           PetscCall(VecDestroy(&v));
838 #else
839           SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
840 #endif
841         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
842       }
843     } else PetscCall(VecLoad_Default(originalv, viewer));
844   }
845   PetscFunctionReturn(PETSC_SUCCESS);
846 }
847 
848 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
849 {
850   PetscSection       coordSection;
851   Vec                coordinates;
852   DMLabel            depthLabel, celltypeLabel;
853   const char        *name[4];
854   const PetscScalar *a;
855   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
856 
857   PetscFunctionBegin;
858   PetscCall(DMGetDimension(dm, &dim));
859   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
860   PetscCall(DMGetCoordinateSection(dm, &coordSection));
861   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
862   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
863   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
864   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
865   PetscCall(VecGetArrayRead(coordinates, &a));
866   name[0]       = "vertex";
867   name[1]       = "edge";
868   name[dim - 1] = "face";
869   name[dim]     = "cell";
870   for (c = cStart; c < cEnd; ++c) {
871     PetscInt *closure = NULL;
872     PetscInt  closureSize, cl, ct;
873 
874     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
875     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
876     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
877     PetscCall(PetscViewerASCIIPushTab(viewer));
878     for (cl = 0; cl < closureSize * 2; cl += 2) {
879       PetscInt point = closure[cl], depth, dof, off, d, p;
880 
881       if ((point < pStart) || (point >= pEnd)) continue;
882       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
883       if (!dof) continue;
884       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
885       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
886       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
887       for (p = 0; p < dof / dim; ++p) {
888         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
889         for (d = 0; d < dim; ++d) {
890           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
891           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double)PetscRealPart(a[off + p * dim + d])));
892         }
893         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
894       }
895       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
896     }
897     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
898     PetscCall(PetscViewerASCIIPopTab(viewer));
899   }
900   PetscCall(VecRestoreArrayRead(coordinates, &a));
901   PetscFunctionReturn(PETSC_SUCCESS);
902 }
903 
904 typedef enum {
905   CS_CARTESIAN,
906   CS_POLAR,
907   CS_CYLINDRICAL,
908   CS_SPHERICAL
909 } CoordSystem;
910 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
911 
912 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
913 {
914   PetscInt i;
915 
916   PetscFunctionBegin;
917   if (dim > 3) {
918     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)PetscRealPart(x[i])));
919   } else {
920     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
921 
922     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
923     switch (cs) {
924     case CS_CARTESIAN:
925       for (i = 0; i < dim; ++i) trcoords[i] = coords[i];
926       break;
927     case CS_POLAR:
928       PetscCheck(dim == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
929       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
930       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
931       break;
932     case CS_CYLINDRICAL:
933       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
934       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
935       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
936       trcoords[2] = coords[2];
937       break;
938     case CS_SPHERICAL:
939       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
940       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
941       trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
942       trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
943       break;
944     }
945     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)trcoords[i]));
946   }
947   PetscFunctionReturn(PETSC_SUCCESS);
948 }
949 
950 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
951 {
952   DM_Plex          *mesh = (DM_Plex *)dm->data;
953   DM                cdm, cdmCell;
954   PetscSection      coordSection, coordSectionCell;
955   Vec               coordinates, coordinatesCell;
956   PetscViewerFormat format;
957 
958   PetscFunctionBegin;
959   PetscCall(PetscViewerGetFormat(viewer, &format));
960   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
961     const char *name;
962     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
963     PetscInt    pStart, pEnd, p, numLabels, l;
964     PetscMPIInt rank, size;
965 
966     PetscCall(DMGetCoordinateDM(dm, &cdm));
967     PetscCall(DMGetCoordinateSection(dm, &coordSection));
968     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
969     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
970     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
971     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
972     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
973     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
974     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
975     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
976     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
977     PetscCall(DMGetDimension(dm, &dim));
978     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
979     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
980     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
981     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
982     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
983     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
984     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
985     for (p = pStart; p < pEnd; ++p) {
986       PetscInt dof, off, s;
987 
988       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
989       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
990       for (s = off; s < off + dof; ++s) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
991     }
992     PetscCall(PetscViewerFlush(viewer));
993     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
994     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
995     for (p = pStart; p < pEnd; ++p) {
996       PetscInt dof, off, c;
997 
998       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
999       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
1000       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]));
1001     }
1002     PetscCall(PetscViewerFlush(viewer));
1003     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1004     if (coordSection && coordinates) {
1005       CoordSystem        cs = CS_CARTESIAN;
1006       const PetscScalar *array, *arrayCell = NULL;
1007       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_INT_MAX, pcEnd = PETSC_INT_MIN, pStart, pEnd, p;
1008       PetscMPIInt        rank;
1009       const char        *name;
1010 
1011       PetscCall(PetscOptionsGetEnum(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *)&cs, NULL));
1012       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
1013       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
1014       PetscCheck(Nf == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
1015       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
1016       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
1017       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
1018       pStart = PetscMin(pvStart, pcStart);
1019       pEnd   = PetscMax(pvEnd, pcEnd);
1020       PetscCall(PetscObjectGetName((PetscObject)coordinates, &name));
1021       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
1022       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
1023       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
1024 
1025       PetscCall(VecGetArrayRead(coordinates, &array));
1026       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
1027       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1028       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
1029       for (p = pStart; p < pEnd; ++p) {
1030         PetscInt dof, off;
1031 
1032         if (p >= pvStart && p < pvEnd) {
1033           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
1034           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
1035           if (dof) {
1036             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dof %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1037             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
1038             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1039           }
1040         }
1041         if (cdmCell && p >= pcStart && p < pcEnd) {
1042           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
1043           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
1044           if (dof) {
1045             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dof %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1046             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
1047             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1048           }
1049         }
1050       }
1051       PetscCall(PetscViewerFlush(viewer));
1052       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1053       PetscCall(VecRestoreArrayRead(coordinates, &array));
1054       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
1055     }
1056     PetscCall(DMGetNumLabels(dm, &numLabels));
1057     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1058     for (l = 0; l < numLabels; ++l) {
1059       DMLabel     label;
1060       PetscBool   isdepth;
1061       const char *name;
1062 
1063       PetscCall(DMGetLabelName(dm, l, &name));
1064       PetscCall(PetscStrcmp(name, "depth", &isdepth));
1065       if (isdepth) continue;
1066       PetscCall(DMGetLabel(dm, name, &label));
1067       PetscCall(DMLabelView(label, viewer));
1068     }
1069     if (size > 1) {
1070       PetscSF sf;
1071 
1072       PetscCall(DMGetPointSF(dm, &sf));
1073       PetscCall(PetscSFView(sf, viewer));
1074     }
1075     if (mesh->periodic.face_sfs)
1076       for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(PetscSFView(mesh->periodic.face_sfs[i], viewer));
1077     PetscCall(PetscViewerFlush(viewer));
1078   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
1079     const char  *name, *color;
1080     const char  *defcolors[3]  = {"gray", "orange", "green"};
1081     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
1082     char         lname[PETSC_MAX_PATH_LEN];
1083     PetscReal    scale      = 2.0;
1084     PetscReal    tikzscale  = 1.0;
1085     PetscBool    useNumbers = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
1086     double       tcoords[3];
1087     PetscScalar *coords;
1088     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;
1089     PetscMPIInt  rank, size;
1090     char       **names, **colors, **lcolors;
1091     PetscBool    flg, lflg;
1092     PetscBT      wp = NULL;
1093     PetscInt     pEnd, pStart;
1094 
1095     PetscCall(DMGetCoordinateDM(dm, &cdm));
1096     PetscCall(DMGetCoordinateSection(dm, &coordSection));
1097     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1098     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
1099     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
1100     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
1101     PetscCall(DMGetDimension(dm, &dim));
1102     PetscCall(DMPlexGetDepth(dm, &depth));
1103     PetscCall(DMGetNumLabels(dm, &numLabels));
1104     numLabels  = PetscMax(numLabels, 10);
1105     numColors  = 10;
1106     numLColors = 10;
1107     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
1108     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
1109     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
1110     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
1111     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
1112     for (d = 0; d < 4; ++d) drawColors[d] = PETSC_TRUE;
1113     n = 4;
1114     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
1115     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1116     n = 4;
1117     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
1118     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1119     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
1120     if (!useLabels) numLabels = 0;
1121     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
1122     if (!useColors) {
1123       numColors = 3;
1124       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
1125     }
1126     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
1127     if (!useColors) {
1128       numLColors = 4;
1129       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
1130     }
1131     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
1132     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
1133     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
1134     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
1135     if (depth < dim) plotEdges = PETSC_FALSE;
1136     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
1137 
1138     /* filter points with labelvalue != labeldefaultvalue */
1139     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1140     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1141     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
1142     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1143     PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
1144     if (lflg) {
1145       DMLabel lbl;
1146 
1147       PetscCall(DMGetLabel(dm, lname, &lbl));
1148       if (lbl) {
1149         PetscInt val, defval;
1150 
1151         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
1152         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
1153         for (c = pStart; c < pEnd; c++) {
1154           PetscInt *closure = NULL;
1155           PetscInt  closureSize;
1156 
1157           PetscCall(DMLabelGetValue(lbl, c, &val));
1158           if (val == defval) continue;
1159 
1160           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1161           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
1162           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1163         }
1164       }
1165     }
1166 
1167     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1168     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1169     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1170     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1171 \\documentclass[tikz]{standalone}\n\n\
1172 \\usepackage{pgflibraryshapes}\n\
1173 \\usetikzlibrary{backgrounds}\n\
1174 \\usetikzlibrary{arrows}\n\
1175 \\begin{document}\n"));
1176     if (size > 1) {
1177       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1178       for (p = 0; p < size; ++p) {
1179         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1180         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1181       }
1182       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1183     }
1184     if (drawHasse) {
1185       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, PetscMax(fEnd - fStart, cEnd - cStart)));
1186 
1187       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1188       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1189       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1190       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1191       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1192       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1193       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1194       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1195       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fStart}{%" PetscInt_FMT "}\n", fStart));
1196       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fEnd}{%" PetscInt_FMT "}\n", fEnd - 1));
1197       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fShift}{%.2f}\n", 3 + (maxStratum - (fEnd - fStart)) / 2.));
1198       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numFaces}{%" PetscInt_FMT "}\n", fEnd - fStart));
1199       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1200       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1201       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1202       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1203     }
1204     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1205 
1206     /* Plot vertices */
1207     PetscCall(VecGetArray(coordinates, &coords));
1208     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1209     for (v = vStart; v < vEnd; ++v) {
1210       PetscInt  off, dof, d;
1211       PetscBool isLabeled = PETSC_FALSE;
1212 
1213       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1214       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1215       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1216       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1217       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1218       for (d = 0; d < dof; ++d) {
1219         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1220         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1221       }
1222       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1223       if (dim == 3) {
1224         PetscReal tmp = tcoords[1];
1225         tcoords[1]    = tcoords[2];
1226         tcoords[2]    = -tmp;
1227       }
1228       for (d = 0; d < dof; ++d) {
1229         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1230         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1231       }
1232       if (drawHasse) color = colors[0 % numColors];
1233       else color = colors[rank % numColors];
1234       for (l = 0; l < numLabels; ++l) {
1235         PetscInt val;
1236         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1237         if (val >= 0) {
1238           color     = lcolors[l % numLColors];
1239           isLabeled = PETSC_TRUE;
1240           break;
1241         }
1242       }
1243       if (drawNumbers[0]) {
1244         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1245       } else if (drawColors[0]) {
1246         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1247       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1248     }
1249     PetscCall(VecRestoreArray(coordinates, &coords));
1250     PetscCall(PetscViewerFlush(viewer));
1251     /* Plot edges */
1252     if (plotEdges) {
1253       PetscCall(VecGetArray(coordinates, &coords));
1254       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1255       for (e = eStart; e < eEnd; ++e) {
1256         const PetscInt *cone;
1257         PetscInt        coneSize, offA, offB, dof, d;
1258 
1259         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1260         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1261         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1262         PetscCall(DMPlexGetCone(dm, e, &cone));
1263         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1264         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1265         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1266         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1267         for (d = 0; d < dof; ++d) {
1268           tcoords[d] = (double)(0.5 * scale * PetscRealPart(coords[offA + d] + coords[offB + d]));
1269           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1270         }
1271         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1272         if (dim == 3) {
1273           PetscReal tmp = tcoords[1];
1274           tcoords[1]    = tcoords[2];
1275           tcoords[2]    = -tmp;
1276         }
1277         for (d = 0; d < dof; ++d) {
1278           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1279           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1280         }
1281         if (drawHasse) color = colors[1 % numColors];
1282         else color = colors[rank % numColors];
1283         for (l = 0; l < numLabels; ++l) {
1284           PetscInt val;
1285           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1286           if (val >= 0) {
1287             color = lcolors[l % numLColors];
1288             break;
1289           }
1290         }
1291         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1292       }
1293       PetscCall(VecRestoreArray(coordinates, &coords));
1294       PetscCall(PetscViewerFlush(viewer));
1295       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1296     }
1297     /* Plot cells */
1298     if (dim == 3 || !drawNumbers[1]) {
1299       for (e = eStart; e < eEnd; ++e) {
1300         const PetscInt *cone;
1301 
1302         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1303         color = colors[rank % numColors];
1304         for (l = 0; l < numLabels; ++l) {
1305           PetscInt val;
1306           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1307           if (val >= 0) {
1308             color = lcolors[l % numLColors];
1309             break;
1310           }
1311         }
1312         PetscCall(DMPlexGetCone(dm, e, &cone));
1313         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1314       }
1315     } else {
1316       DMPolytopeType ct;
1317 
1318       /* Drawing a 2D polygon */
1319       for (c = cStart; c < cEnd; ++c) {
1320         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1321         PetscCall(DMPlexGetCellType(dm, c, &ct));
1322         if (DMPolytopeTypeIsHybrid(ct)) {
1323           const PetscInt *cone;
1324           PetscInt        coneSize, e;
1325 
1326           PetscCall(DMPlexGetCone(dm, c, &cone));
1327           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1328           for (e = 0; e < coneSize; ++e) {
1329             const PetscInt *econe;
1330 
1331             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1332             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));
1333           }
1334         } else {
1335           PetscInt *closure = NULL;
1336           PetscInt  closureSize, Nv = 0, v;
1337 
1338           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1339           for (p = 0; p < closureSize * 2; p += 2) {
1340             const PetscInt point = closure[p];
1341 
1342             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1343           }
1344           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1345           for (v = 0; v <= Nv; ++v) {
1346             const PetscInt vertex = closure[v % Nv];
1347 
1348             if (v > 0) {
1349               if (plotEdges) {
1350                 const PetscInt *edge;
1351                 PetscInt        endpoints[2], ne;
1352 
1353                 endpoints[0] = closure[v - 1];
1354                 endpoints[1] = vertex;
1355                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1356                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1357                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1358                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1359               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1360             }
1361             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1362           }
1363           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1364           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1365         }
1366       }
1367     }
1368     for (c = cStart; c < cEnd; ++c) {
1369       double             ccoords[3] = {0.0, 0.0, 0.0};
1370       PetscBool          isLabeled  = PETSC_FALSE;
1371       PetscScalar       *cellCoords = NULL;
1372       const PetscScalar *array;
1373       PetscInt           numCoords, cdim, d;
1374       PetscBool          isDG;
1375 
1376       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1377       PetscCall(DMGetCoordinateDim(dm, &cdim));
1378       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1379       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1380       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1381       for (p = 0; p < numCoords / cdim; ++p) {
1382         for (d = 0; d < cdim; ++d) {
1383           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1384           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1385         }
1386         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1387         if (cdim == 3) {
1388           PetscReal tmp = tcoords[1];
1389           tcoords[1]    = tcoords[2];
1390           tcoords[2]    = -tmp;
1391         }
1392         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1393       }
1394       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1395       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1396       for (d = 0; d < cdim; ++d) {
1397         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1398         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)ccoords[d]));
1399       }
1400       if (drawHasse) color = colors[depth % numColors];
1401       else color = colors[rank % numColors];
1402       for (l = 0; l < numLabels; ++l) {
1403         PetscInt val;
1404         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1405         if (val >= 0) {
1406           color     = lcolors[l % numLColors];
1407           isLabeled = PETSC_TRUE;
1408           break;
1409         }
1410       }
1411       if (drawNumbers[dim]) {
1412         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1413       } else if (drawColors[dim]) {
1414         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1415       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1416     }
1417     if (drawHasse) {
1418       int height = 0;
1419 
1420       color = colors[depth % numColors];
1421       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1422       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1423       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1424       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,%d) {\\c};\n", rank, color, height++));
1425       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1426 
1427       if (depth > 2) {
1428         color = colors[1 % numColors];
1429         PetscCall(PetscViewerASCIIPrintf(viewer, "%% Faces\n"));
1430         PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\f in {\\fStart,...,\\fEnd}\n"));
1431         PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1432         PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\f_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\fShift+\\f-\\fStart,%d) {\\f};\n", rank, color, height++));
1433         PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1434       }
1435 
1436       color = colors[1 % numColors];
1437       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1438       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1439       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1440       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,%d) {\\e};\n", rank, color, height++));
1441       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1442 
1443       color = colors[0 % numColors];
1444       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1445       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1446       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1447       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,%d) {\\v};\n", rank, color, height++));
1448       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1449 
1450       for (p = pStart; p < pEnd; ++p) {
1451         const PetscInt *cone;
1452         PetscInt        coneSize, cp;
1453 
1454         PetscCall(DMPlexGetCone(dm, p, &cone));
1455         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1456         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1457       }
1458     }
1459     PetscCall(PetscViewerFlush(viewer));
1460     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1461     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1462     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1463     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1464     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1465     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1466     PetscCall(PetscFree3(names, colors, lcolors));
1467     PetscCall(PetscBTDestroy(&wp));
1468   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1469     Vec                    cown, acown;
1470     VecScatter             sct;
1471     ISLocalToGlobalMapping g2l;
1472     IS                     gid, acis;
1473     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1474     MPI_Group              ggroup, ngroup;
1475     PetscScalar           *array, nid;
1476     const PetscInt        *idxs;
1477     PetscInt              *idxs2, *start, *adjacency, *work;
1478     PetscInt64             lm[3], gm[3];
1479     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1480     PetscMPIInt            d1, d2, rank;
1481 
1482     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1483     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1484 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1485     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1486 #endif
1487     if (ncomm != MPI_COMM_NULL) {
1488       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1489       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1490       d1 = 0;
1491       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1492       nid = d2;
1493       PetscCallMPI(MPI_Group_free(&ggroup));
1494       PetscCallMPI(MPI_Group_free(&ngroup));
1495       PetscCallMPI(MPI_Comm_free(&ncomm));
1496     } else nid = 0.0;
1497 
1498     /* Get connectivity */
1499     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1500     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1501 
1502     /* filter overlapped local cells */
1503     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1504     PetscCall(ISGetIndices(gid, &idxs));
1505     PetscCall(ISGetLocalSize(gid, &cum));
1506     PetscCall(PetscMalloc1(cum, &idxs2));
1507     for (c = cStart, cum = 0; c < cEnd; c++) {
1508       if (idxs[c - cStart] < 0) continue;
1509       idxs2[cum++] = idxs[c - cStart];
1510     }
1511     PetscCall(ISRestoreIndices(gid, &idxs));
1512     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1513     PetscCall(ISDestroy(&gid));
1514     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1515 
1516     /* support for node-aware cell locality */
1517     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1518     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1519     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1520     PetscCall(VecGetArray(cown, &array));
1521     for (c = 0; c < numVertices; c++) array[c] = nid;
1522     PetscCall(VecRestoreArray(cown, &array));
1523     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1524     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1525     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1526     PetscCall(ISDestroy(&acis));
1527     PetscCall(VecScatterDestroy(&sct));
1528     PetscCall(VecDestroy(&cown));
1529 
1530     /* compute edgeCut */
1531     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1532     PetscCall(PetscMalloc1(cum, &work));
1533     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1534     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1535     PetscCall(ISDestroy(&gid));
1536     PetscCall(VecGetArray(acown, &array));
1537     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1538       PetscInt totl;
1539 
1540       totl = start[c + 1] - start[c];
1541       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1542       for (i = 0; i < totl; i++) {
1543         if (work[i] < 0) {
1544           ect += 1;
1545           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1546         }
1547       }
1548     }
1549     PetscCall(PetscFree(work));
1550     PetscCall(VecRestoreArray(acown, &array));
1551     lm[0] = numVertices > 0 ? numVertices : PETSC_INT_MAX;
1552     lm[1] = -numVertices;
1553     PetscCallMPI(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1554     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT, -((double)gm[1]) / ((double)gm[0]), -(PetscInt)gm[1], (PetscInt)gm[0]));
1555     lm[0] = ect;                     /* edgeCut */
1556     lm[1] = ectn;                    /* node-aware edgeCut */
1557     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1558     PetscCallMPI(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1559     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt_FMT ")\n", (PetscInt)gm[2]));
1560 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1561     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), gm[0] ? ((double)gm[1]) / ((double)gm[0]) : 1.));
1562 #else
1563     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), 0.0));
1564 #endif
1565     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1566     PetscCall(PetscFree(start));
1567     PetscCall(PetscFree(adjacency));
1568     PetscCall(VecDestroy(&acown));
1569   } else {
1570     const char    *name;
1571     PetscInt      *sizes, *hybsizes, *ghostsizes;
1572     PetscInt       locDepth, depth, cellHeight, dim, d;
1573     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1574     PetscInt       numLabels, l, maxSize = 17;
1575     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1576     MPI_Comm       comm;
1577     PetscMPIInt    size, rank;
1578 
1579     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1580     PetscCallMPI(MPI_Comm_size(comm, &size));
1581     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1582     PetscCall(DMGetDimension(dm, &dim));
1583     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1584     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1585     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1586     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1587     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1588     PetscCall(DMPlexGetDepth(dm, &locDepth));
1589     PetscCallMPI(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1590     PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &gcStart, &gcEnd));
1591     gcNum = gcEnd - gcStart;
1592     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1593     else PetscCall(PetscCalloc3(3, &sizes, 3, &hybsizes, 3, &ghostsizes));
1594     for (d = 0; d <= depth; d++) {
1595       PetscInt Nc[2] = {0, 0}, ict;
1596 
1597       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1598       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1599       ict = ct0;
1600       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1601       ct0 = (DMPolytopeType)ict;
1602       for (p = pStart; p < pEnd; ++p) {
1603         DMPolytopeType ct;
1604 
1605         PetscCall(DMPlexGetCellType(dm, p, &ct));
1606         if (ct == ct0) ++Nc[0];
1607         else ++Nc[1];
1608       }
1609       if (size < maxSize) {
1610         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes, 1, MPIU_INT, 0, comm));
1611         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1612         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1613         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1614         for (p = 0; p < size; ++p) {
1615           if (rank == 0) {
1616             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p] + hybsizes[p]));
1617             if (hybsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1618             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1619           }
1620         }
1621       } else {
1622         PetscInt locMinMax[2];
1623 
1624         locMinMax[0] = Nc[0] + Nc[1];
1625         locMinMax[1] = Nc[0] + Nc[1];
1626         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1627         locMinMax[0] = Nc[1];
1628         locMinMax[1] = Nc[1];
1629         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1630         if (d == depth) {
1631           locMinMax[0] = gcNum;
1632           locMinMax[1] = gcNum;
1633           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1634         }
1635         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1636         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1637         if (hybsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1638         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1639       }
1640       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1641     }
1642     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1643     {
1644       const PetscReal *maxCell;
1645       const PetscReal *L;
1646       PetscBool        localized;
1647 
1648       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1649       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1650       if (L || localized) {
1651         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1652         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1653         if (L) {
1654           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1655           for (d = 0; d < dim; ++d) {
1656             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1657             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1658           }
1659           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1660         }
1661         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1662         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1663       }
1664     }
1665     PetscCall(DMGetNumLabels(dm, &numLabels));
1666     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1667     for (l = 0; l < numLabels; ++l) {
1668       DMLabel     label;
1669       const char *name;
1670       PetscInt   *values;
1671       PetscInt    numValues, v;
1672 
1673       PetscCall(DMGetLabelName(dm, l, &name));
1674       PetscCall(DMGetLabel(dm, name, &label));
1675       PetscCall(DMLabelGetNumValues(label, &numValues));
1676       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1677 
1678       { // Extract array of DMLabel values so it can be sorted
1679         IS              is_values;
1680         const PetscInt *is_values_local = NULL;
1681 
1682         PetscCall(DMLabelGetValueIS(label, &is_values));
1683         PetscCall(ISGetIndices(is_values, &is_values_local));
1684         PetscCall(PetscMalloc1(numValues, &values));
1685         PetscCall(PetscArraycpy(values, is_values_local, numValues));
1686         PetscCall(PetscSortInt(numValues, values));
1687         PetscCall(ISRestoreIndices(is_values, &is_values_local));
1688         PetscCall(ISDestroy(&is_values));
1689       }
1690       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1691       for (v = 0; v < numValues; ++v) {
1692         PetscInt size;
1693 
1694         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1695         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1696         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1697       }
1698       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1699       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1700       PetscCall(PetscFree(values));
1701     }
1702     {
1703       char    **labelNames;
1704       PetscInt  Nl = numLabels;
1705       PetscBool flg;
1706 
1707       PetscCall(PetscMalloc1(Nl, &labelNames));
1708       PetscCall(PetscOptionsGetStringArray(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1709       for (l = 0; l < Nl; ++l) {
1710         DMLabel label;
1711 
1712         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1713         if (flg) {
1714           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1715           PetscCall(DMLabelView(label, viewer));
1716         }
1717         PetscCall(PetscFree(labelNames[l]));
1718       }
1719       PetscCall(PetscFree(labelNames));
1720     }
1721     /* If no fields are specified, people do not want to see adjacency */
1722     if (dm->Nf) {
1723       PetscInt f;
1724 
1725       for (f = 0; f < dm->Nf; ++f) {
1726         const char *name;
1727 
1728         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1729         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1730         PetscCall(PetscViewerASCIIPushTab(viewer));
1731         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1732         if (dm->fields[f].adjacency[0]) {
1733           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1734           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1735         } else {
1736           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1737           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1738         }
1739         PetscCall(PetscViewerASCIIPopTab(viewer));
1740       }
1741     }
1742     PetscCall(DMGetCoarseDM(dm, &cdm));
1743     if (cdm) {
1744       PetscCall(PetscViewerASCIIPushTab(viewer));
1745       PetscCall(PetscViewerASCIIPrintf(viewer, "Defined by transform from:\n"));
1746       PetscCall(DMPlexView_Ascii(cdm, viewer));
1747       PetscCall(PetscViewerASCIIPopTab(viewer));
1748     }
1749   }
1750   PetscFunctionReturn(PETSC_SUCCESS);
1751 }
1752 
1753 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1754 {
1755   DMPolytopeType ct;
1756   PetscMPIInt    rank;
1757   PetscInt       cdim;
1758 
1759   PetscFunctionBegin;
1760   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1761   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1762   PetscCall(DMGetCoordinateDim(dm, &cdim));
1763   switch (ct) {
1764   case DM_POLYTOPE_SEGMENT:
1765   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1766     switch (cdim) {
1767     case 1: {
1768       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1769       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1770 
1771       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, PETSC_DRAW_BLACK));
1772       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, PETSC_DRAW_BLACK));
1773       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, PETSC_DRAW_BLACK));
1774     } break;
1775     case 2: {
1776       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1777       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1778       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1779 
1780       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1781       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));
1782       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));
1783     } break;
1784     default:
1785       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1786     }
1787     break;
1788   case DM_POLYTOPE_TRIANGLE:
1789     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));
1790     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1791     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1792     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1793     break;
1794   case DM_POLYTOPE_QUADRILATERAL:
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[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));
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[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1799     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1800     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1801     break;
1802   case DM_POLYTOPE_SEG_PRISM_TENSOR:
1803     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));
1804     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));
1805     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1806     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1807     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1808     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1809     break;
1810   case DM_POLYTOPE_FV_GHOST:
1811     break;
1812   default:
1813     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1814   }
1815   PetscFunctionReturn(PETSC_SUCCESS);
1816 }
1817 
1818 static PetscErrorCode DrawPolygon_Private(DM dm, PetscDraw draw, PetscInt cell, PetscInt Nv, const PetscReal refVertices[], const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1819 {
1820   PetscReal   centroid[2] = {0., 0.};
1821   PetscMPIInt rank;
1822   PetscMPIInt fillColor;
1823 
1824   PetscFunctionBegin;
1825   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1826   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1827   for (PetscInt v = 0; v < Nv; ++v) {
1828     centroid[0] += PetscRealPart(coords[v * 2 + 0]) / Nv;
1829     centroid[1] += PetscRealPart(coords[v * 2 + 1]) / Nv;
1830   }
1831   for (PetscInt e = 0; e < Nv; ++e) {
1832     refCoords[0] = refVertices[e * 2 + 0];
1833     refCoords[1] = refVertices[e * 2 + 1];
1834     for (PetscInt d = 1; d <= edgeDiv; ++d) {
1835       refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % Nv * 2 + 0] - refCoords[0]) * d / edgeDiv;
1836       refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % Nv * 2 + 1] - refCoords[1]) * d / edgeDiv;
1837     }
1838     PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1839     for (PetscInt d = 0; d < edgeDiv; ++d) {
1840       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));
1841       PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1842     }
1843   }
1844   PetscFunctionReturn(PETSC_SUCCESS);
1845 }
1846 
1847 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1848 {
1849   DMPolytopeType ct;
1850 
1851   PetscFunctionBegin;
1852   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1853   switch (ct) {
1854   case DM_POLYTOPE_TRIANGLE: {
1855     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1856 
1857     PetscCall(DrawPolygon_Private(dm, draw, cell, 3, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1858   } break;
1859   case DM_POLYTOPE_QUADRILATERAL: {
1860     PetscReal refVertices[8] = {-1., -1., 1., -1., 1., 1., -1., 1.};
1861 
1862     PetscCall(DrawPolygon_Private(dm, draw, cell, 4, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1863   } break;
1864   default:
1865     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1866   }
1867   PetscFunctionReturn(PETSC_SUCCESS);
1868 }
1869 
1870 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1871 {
1872   PetscDraw    draw;
1873   DM           cdm;
1874   PetscSection coordSection;
1875   Vec          coordinates;
1876   PetscReal    xyl[3], xyr[3];
1877   PetscReal   *refCoords, *edgeCoords;
1878   PetscBool    isnull, drawAffine;
1879   PetscInt     dim, vStart, vEnd, cStart, cEnd, c, cDegree, edgeDiv;
1880 
1881   PetscFunctionBegin;
1882   PetscCall(DMGetCoordinateDim(dm, &dim));
1883   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1884   PetscCall(DMGetCoordinateDegree_Internal(dm, &cDegree));
1885   drawAffine = cDegree > 1 ? PETSC_FALSE : PETSC_TRUE;
1886   edgeDiv    = cDegree + 1;
1887   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1888   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1889   PetscCall(DMGetCoordinateDM(dm, &cdm));
1890   PetscCall(DMGetLocalSection(cdm, &coordSection));
1891   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1892   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1893   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1894 
1895   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1896   PetscCall(PetscDrawIsNull(draw, &isnull));
1897   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
1898   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1899 
1900   PetscCall(DMGetBoundingBox(dm, xyl, xyr));
1901   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1902   PetscCall(PetscDrawClear(draw));
1903 
1904   for (c = cStart; c < cEnd; ++c) {
1905     PetscScalar       *coords = NULL;
1906     const PetscScalar *coords_arr;
1907     PetscInt           numCoords;
1908     PetscBool          isDG;
1909 
1910     PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1911     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1912     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1913     PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1914   }
1915   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1916   PetscCall(PetscDrawFlush(draw));
1917   PetscCall(PetscDrawPause(draw));
1918   PetscCall(PetscDrawSave(draw));
1919   PetscFunctionReturn(PETSC_SUCCESS);
1920 }
1921 
1922 static PetscErrorCode DMPlexCreateHighOrderSurrogate_Internal(DM dm, DM *hdm)
1923 {
1924   DM           odm = dm, rdm = dm, cdm;
1925   PetscFE      fe;
1926   PetscSpace   sp;
1927   PetscClassId id;
1928   PetscInt     degree;
1929   PetscBool    hoView = PETSC_TRUE;
1930 
1931   PetscFunctionBegin;
1932   PetscObjectOptionsBegin((PetscObject)dm);
1933   PetscCall(PetscOptionsBool("-dm_plex_high_order_view", "Subsample to view meshes with high order coordinates", "DMPlexCreateHighOrderSurrogate_Internal", hoView, &hoView, NULL));
1934   PetscOptionsEnd();
1935   PetscCall(PetscObjectReference((PetscObject)dm));
1936   *hdm = dm;
1937   if (!hoView) PetscFunctionReturn(PETSC_SUCCESS);
1938   PetscCall(DMGetCoordinateDM(dm, &cdm));
1939   PetscCall(DMGetField(cdm, 0, NULL, (PetscObject *)&fe));
1940   PetscCall(PetscObjectGetClassId((PetscObject)fe, &id));
1941   if (id != PETSCFE_CLASSID) PetscFunctionReturn(PETSC_SUCCESS);
1942   PetscCall(PetscFEGetBasisSpace(fe, &sp));
1943   PetscCall(PetscSpaceGetDegree(sp, &degree, NULL));
1944   for (PetscInt r = 0, rd = PetscCeilReal(((PetscReal)degree) / 2.); r < (PetscInt)PetscCeilReal(PetscLog2Real(degree)); ++r, rd = PetscCeilReal(((PetscReal)rd) / 2.)) {
1945     DM  cdm, rcdm;
1946     Mat In;
1947     Vec cl, rcl;
1948 
1949     PetscCall(DMRefine(odm, PetscObjectComm((PetscObject)odm), &rdm));
1950     PetscCall(DMPlexCreateCoordinateSpace(rdm, rd, PETSC_FALSE, NULL));
1951     PetscCall(PetscObjectSetName((PetscObject)rdm, "Refined Mesh with Linear Coordinates"));
1952     PetscCall(DMGetCoordinateDM(odm, &cdm));
1953     PetscCall(DMGetCoordinateDM(rdm, &rcdm));
1954     PetscCall(DMGetCoordinatesLocal(odm, &cl));
1955     PetscCall(DMGetCoordinatesLocal(rdm, &rcl));
1956     PetscCall(DMSetCoarseDM(rcdm, cdm));
1957     PetscCall(DMCreateInterpolation(cdm, rcdm, &In, NULL));
1958     PetscCall(MatMult(In, cl, rcl));
1959     PetscCall(MatDestroy(&In));
1960     PetscCall(DMSetCoordinatesLocal(rdm, rcl));
1961     PetscCall(DMDestroy(&odm));
1962     odm = rdm;
1963   }
1964   *hdm = rdm;
1965   PetscFunctionReturn(PETSC_SUCCESS);
1966 }
1967 
1968 #if defined(PETSC_HAVE_EXODUSII)
1969   #include <exodusII.h>
1970   #include <petscviewerexodusii.h>
1971 #endif
1972 
1973 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1974 {
1975   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns;
1976   char      name[PETSC_MAX_PATH_LEN];
1977 
1978   PetscFunctionBegin;
1979   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1980   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1981   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
1982   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
1983   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1984   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
1985   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
1986   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
1987   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
1988   if (iascii) {
1989     PetscViewerFormat format;
1990     PetscCall(PetscViewerGetFormat(viewer, &format));
1991     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1992     else PetscCall(DMPlexView_Ascii(dm, viewer));
1993   } else if (ishdf5) {
1994 #if defined(PETSC_HAVE_HDF5)
1995     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1996 #else
1997     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1998 #endif
1999   } else if (isvtk) {
2000     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
2001   } else if (isdraw) {
2002     DM hdm;
2003 
2004     PetscCall(DMPlexCreateHighOrderSurrogate_Internal(dm, &hdm));
2005     PetscCall(DMPlexView_Draw(hdm, viewer));
2006     PetscCall(DMDestroy(&hdm));
2007   } else if (isglvis) {
2008     PetscCall(DMPlexView_GLVis(dm, viewer));
2009 #if defined(PETSC_HAVE_EXODUSII)
2010   } else if (isexodus) {
2011     /*
2012       exodusII requires that all sets be part of exactly one cell set.
2013       If the dm does not have a "Cell Sets" label defined, we create one
2014       with ID 1, containing all cells.
2015       Note that if the Cell Sets label is defined but does not cover all cells,
2016       we may still have a problem. This should probably be checked here or in the viewer;
2017     */
2018     PetscInt numCS;
2019     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
2020     if (!numCS) {
2021       PetscInt cStart, cEnd, c;
2022       PetscCall(DMCreateLabel(dm, "Cell Sets"));
2023       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
2024       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
2025     }
2026     PetscCall(DMView_PlexExodusII(dm, viewer));
2027 #endif
2028 #if defined(PETSC_HAVE_CGNS)
2029   } else if (iscgns) {
2030     PetscCall(DMView_PlexCGNS(dm, viewer));
2031 #endif
2032   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
2033   /* Optionally view the partition */
2034   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
2035   if (flg) {
2036     Vec ranks;
2037     PetscCall(DMPlexCreateRankField(dm, &ranks));
2038     PetscCall(VecView(ranks, viewer));
2039     PetscCall(VecDestroy(&ranks));
2040   }
2041   /* Optionally view a label */
2042   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
2043   if (flg) {
2044     DMLabel label;
2045     Vec     val;
2046 
2047     PetscCall(DMGetLabel(dm, name, &label));
2048     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
2049     PetscCall(DMPlexCreateLabelField(dm, label, &val));
2050     PetscCall(VecView(val, viewer));
2051     PetscCall(VecDestroy(&val));
2052   }
2053   PetscFunctionReturn(PETSC_SUCCESS);
2054 }
2055 
2056 /*@
2057   DMPlexTopologyView - Saves a `DMPLEX` topology into a file
2058 
2059   Collective
2060 
2061   Input Parameters:
2062 + dm     - The `DM` whose topology is to be saved
2063 - viewer - The `PetscViewer` to save it in
2064 
2065   Level: advanced
2066 
2067 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`, `PetscViewer`
2068 @*/
2069 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
2070 {
2071   PetscBool ishdf5;
2072 
2073   PetscFunctionBegin;
2074   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2075   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2076   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2077   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
2078   if (ishdf5) {
2079 #if defined(PETSC_HAVE_HDF5)
2080     PetscViewerFormat format;
2081     PetscCall(PetscViewerGetFormat(viewer, &format));
2082     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2083       IS globalPointNumbering;
2084 
2085       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2086       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
2087       PetscCall(ISDestroy(&globalPointNumbering));
2088     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2089 #else
2090     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2091 #endif
2092   }
2093   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
2094   PetscFunctionReturn(PETSC_SUCCESS);
2095 }
2096 
2097 /*@
2098   DMPlexCoordinatesView - Saves `DMPLEX` coordinates into a file
2099 
2100   Collective
2101 
2102   Input Parameters:
2103 + dm     - The `DM` whose coordinates are to be saved
2104 - viewer - The `PetscViewer` for saving
2105 
2106   Level: advanced
2107 
2108 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`, `PetscViewer`
2109 @*/
2110 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
2111 {
2112   PetscBool ishdf5;
2113 
2114   PetscFunctionBegin;
2115   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2116   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2117   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2118   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2119   if (ishdf5) {
2120 #if defined(PETSC_HAVE_HDF5)
2121     PetscViewerFormat format;
2122     PetscCall(PetscViewerGetFormat(viewer, &format));
2123     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2124       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
2125     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2126 #else
2127     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2128 #endif
2129   }
2130   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2131   PetscFunctionReturn(PETSC_SUCCESS);
2132 }
2133 
2134 /*@
2135   DMPlexLabelsView - Saves `DMPLEX` labels into a file
2136 
2137   Collective
2138 
2139   Input Parameters:
2140 + dm     - The `DM` whose labels are to be saved
2141 - viewer - The `PetscViewer` for saving
2142 
2143   Level: advanced
2144 
2145 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`, `PetscViewer`
2146 @*/
2147 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
2148 {
2149   PetscBool ishdf5;
2150 
2151   PetscFunctionBegin;
2152   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2153   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2154   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2155   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
2156   if (ishdf5) {
2157 #if defined(PETSC_HAVE_HDF5)
2158     IS                globalPointNumbering;
2159     PetscViewerFormat format;
2160 
2161     PetscCall(PetscViewerGetFormat(viewer, &format));
2162     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2163       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2164       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
2165       PetscCall(ISDestroy(&globalPointNumbering));
2166     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2167 #else
2168     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2169 #endif
2170   }
2171   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
2172   PetscFunctionReturn(PETSC_SUCCESS);
2173 }
2174 
2175 /*@
2176   DMPlexSectionView - Saves a section associated with a `DMPLEX`
2177 
2178   Collective
2179 
2180   Input Parameters:
2181 + dm        - The `DM` that contains the topology on which the section to be saved is defined
2182 . viewer    - The `PetscViewer` for saving
2183 - sectiondm - The `DM` that contains the section to be saved, can be `NULL`
2184 
2185   Level: advanced
2186 
2187   Notes:
2188   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.
2189 
2190   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.
2191 
2192 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`, `PetscViewer`
2193 @*/
2194 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
2195 {
2196   PetscBool ishdf5;
2197 
2198   PetscFunctionBegin;
2199   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2200   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2201   if (!sectiondm) sectiondm = dm;
2202   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2203   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2204   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
2205   if (ishdf5) {
2206 #if defined(PETSC_HAVE_HDF5)
2207     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
2208 #else
2209     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2210 #endif
2211   }
2212   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
2213   PetscFunctionReturn(PETSC_SUCCESS);
2214 }
2215 
2216 /*@
2217   DMPlexGlobalVectorView - Saves a global vector
2218 
2219   Collective
2220 
2221   Input Parameters:
2222 + dm        - The `DM` that represents the topology
2223 . viewer    - The `PetscViewer` to save data with
2224 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2225 - vec       - The global vector to be saved
2226 
2227   Level: advanced
2228 
2229   Notes:
2230   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.
2231 
2232   Calling sequence:
2233 .vb
2234        DMCreate(PETSC_COMM_WORLD, &dm);
2235        DMSetType(dm, DMPLEX);
2236        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2237        DMClone(dm, &sectiondm);
2238        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2239        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2240        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2241        PetscSectionSetChart(section, pStart, pEnd);
2242        PetscSectionSetUp(section);
2243        DMSetLocalSection(sectiondm, section);
2244        PetscSectionDestroy(&section);
2245        DMGetGlobalVector(sectiondm, &vec);
2246        PetscObjectSetName((PetscObject)vec, "vec_name");
2247        DMPlexTopologyView(dm, viewer);
2248        DMPlexSectionView(dm, viewer, sectiondm);
2249        DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
2250        DMRestoreGlobalVector(sectiondm, &vec);
2251        DMDestroy(&sectiondm);
2252        DMDestroy(&dm);
2253 .ve
2254 
2255 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2256 @*/
2257 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2258 {
2259   PetscBool ishdf5;
2260 
2261   PetscFunctionBegin;
2262   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2263   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2264   if (!sectiondm) sectiondm = dm;
2265   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2266   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2267   /* Check consistency */
2268   {
2269     PetscSection section;
2270     PetscBool    includesConstraints;
2271     PetscInt     m, m1;
2272 
2273     PetscCall(VecGetLocalSize(vec, &m1));
2274     PetscCall(DMGetGlobalSection(sectiondm, &section));
2275     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2276     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2277     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2278     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2279   }
2280   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2281   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2282   if (ishdf5) {
2283 #if defined(PETSC_HAVE_HDF5)
2284     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2285 #else
2286     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2287 #endif
2288   }
2289   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2290   PetscFunctionReturn(PETSC_SUCCESS);
2291 }
2292 
2293 /*@
2294   DMPlexLocalVectorView - Saves a local vector
2295 
2296   Collective
2297 
2298   Input Parameters:
2299 + dm        - The `DM` that represents the topology
2300 . viewer    - The `PetscViewer` to save data with
2301 . sectiondm - The `DM` that contains the local section on which `vec` is defined, can be `NULL`
2302 - vec       - The local vector to be saved
2303 
2304   Level: advanced
2305 
2306   Note:
2307   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.
2308 
2309   Calling sequence:
2310 .vb
2311        DMCreate(PETSC_COMM_WORLD, &dm);
2312        DMSetType(dm, DMPLEX);
2313        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2314        DMClone(dm, &sectiondm);
2315        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2316        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2317        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2318        PetscSectionSetChart(section, pStart, pEnd);
2319        PetscSectionSetUp(section);
2320        DMSetLocalSection(sectiondm, section);
2321        DMGetLocalVector(sectiondm, &vec);
2322        PetscObjectSetName((PetscObject)vec, "vec_name");
2323        DMPlexTopologyView(dm, viewer);
2324        DMPlexSectionView(dm, viewer, sectiondm);
2325        DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2326        DMRestoreLocalVector(sectiondm, &vec);
2327        DMDestroy(&sectiondm);
2328        DMDestroy(&dm);
2329 .ve
2330 
2331 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2332 @*/
2333 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2334 {
2335   PetscBool ishdf5;
2336 
2337   PetscFunctionBegin;
2338   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2339   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2340   if (!sectiondm) sectiondm = dm;
2341   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2342   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2343   /* Check consistency */
2344   {
2345     PetscSection section;
2346     PetscBool    includesConstraints;
2347     PetscInt     m, m1;
2348 
2349     PetscCall(VecGetLocalSize(vec, &m1));
2350     PetscCall(DMGetLocalSection(sectiondm, &section));
2351     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2352     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2353     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2354     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2355   }
2356   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2357   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2358   if (ishdf5) {
2359 #if defined(PETSC_HAVE_HDF5)
2360     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2361 #else
2362     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2363 #endif
2364   }
2365   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2366   PetscFunctionReturn(PETSC_SUCCESS);
2367 }
2368 
2369 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2370 {
2371   PetscBool ishdf5;
2372 
2373   PetscFunctionBegin;
2374   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2375   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2376   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2377   if (ishdf5) {
2378 #if defined(PETSC_HAVE_HDF5)
2379     PetscViewerFormat format;
2380     PetscCall(PetscViewerGetFormat(viewer, &format));
2381     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2382       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2383     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2384       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2385     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2386     PetscFunctionReturn(PETSC_SUCCESS);
2387 #else
2388     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2389 #endif
2390   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2391 }
2392 
2393 /*@
2394   DMPlexTopologyLoad - Loads a topology into a `DMPLEX`
2395 
2396   Collective
2397 
2398   Input Parameters:
2399 + dm     - The `DM` into which the topology is loaded
2400 - viewer - The `PetscViewer` for the saved topology
2401 
2402   Output Parameter:
2403 . 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;
2404   `NULL` if unneeded
2405 
2406   Level: advanced
2407 
2408 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2409           `PetscViewer`, `PetscSF`
2410 @*/
2411 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2412 {
2413   PetscBool ishdf5;
2414 
2415   PetscFunctionBegin;
2416   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2417   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2418   if (globalToLocalPointSF) PetscAssertPointer(globalToLocalPointSF, 3);
2419   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2420   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2421   if (ishdf5) {
2422 #if defined(PETSC_HAVE_HDF5)
2423     PetscViewerFormat format;
2424     PetscCall(PetscViewerGetFormat(viewer, &format));
2425     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2426       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2427     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2428 #else
2429     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2430 #endif
2431   }
2432   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2433   PetscFunctionReturn(PETSC_SUCCESS);
2434 }
2435 
2436 /*@
2437   DMPlexCoordinatesLoad - Loads coordinates into a `DMPLEX`
2438 
2439   Collective
2440 
2441   Input Parameters:
2442 + dm                   - The `DM` into which the coordinates are loaded
2443 . viewer               - The `PetscViewer` for the saved coordinates
2444 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2445 
2446   Level: advanced
2447 
2448 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2449           `PetscSF`, `PetscViewer`
2450 @*/
2451 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2452 {
2453   PetscBool ishdf5;
2454 
2455   PetscFunctionBegin;
2456   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2457   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2458   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2459   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2460   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2461   if (ishdf5) {
2462 #if defined(PETSC_HAVE_HDF5)
2463     PetscViewerFormat format;
2464     PetscCall(PetscViewerGetFormat(viewer, &format));
2465     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2466       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2467     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2468 #else
2469     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2470 #endif
2471   }
2472   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2473   PetscFunctionReturn(PETSC_SUCCESS);
2474 }
2475 
2476 /*@
2477   DMPlexLabelsLoad - Loads labels into a `DMPLEX`
2478 
2479   Collective
2480 
2481   Input Parameters:
2482 + dm                   - The `DM` into which the labels are loaded
2483 . viewer               - The `PetscViewer` for the saved labels
2484 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading `dm` from viewer
2485 
2486   Level: advanced
2487 
2488   Note:
2489   The `PetscSF` argument must not be `NULL` if the `DM` is distributed, otherwise an error occurs.
2490 
2491 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2492           `PetscSF`, `PetscViewer`
2493 @*/
2494 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2495 {
2496   PetscBool ishdf5;
2497 
2498   PetscFunctionBegin;
2499   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2500   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2501   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2502   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2503   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2504   if (ishdf5) {
2505 #if defined(PETSC_HAVE_HDF5)
2506     PetscViewerFormat format;
2507 
2508     PetscCall(PetscViewerGetFormat(viewer, &format));
2509     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2510       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2511     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2512 #else
2513     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2514 #endif
2515   }
2516   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2517   PetscFunctionReturn(PETSC_SUCCESS);
2518 }
2519 
2520 /*@
2521   DMPlexSectionLoad - Loads section into a `DMPLEX`
2522 
2523   Collective
2524 
2525   Input Parameters:
2526 + dm                   - The `DM` that represents the topology
2527 . viewer               - The `PetscViewer` that represents the on-disk section (sectionA)
2528 . sectiondm            - The `DM` into which the on-disk section (sectionA) is migrated, can be `NULL`
2529 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad(`) when loading dm from viewer
2530 
2531   Output Parameters:
2532 + 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)
2533 - 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)
2534 
2535   Level: advanced
2536 
2537   Notes:
2538   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.
2539 
2540   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.
2541 
2542   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.
2543 
2544   Example using 2 processes:
2545 .vb
2546   NX (number of points on dm): 4
2547   sectionA                   : the on-disk section
2548   vecA                       : a vector associated with sectionA
2549   sectionB                   : sectiondm's local section constructed in this function
2550   vecB (local)               : a vector associated with sectiondm's local section
2551   vecB (global)              : a vector associated with sectiondm's global section
2552 
2553                                      rank 0    rank 1
2554   vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2555   sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2556   sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2557   sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2558   [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2559   sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2560   sectionB->atlasDof             :     1 0 1 | 1 3
2561   sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2562   vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2563   vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2564 .ve
2565   where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2566 
2567 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`, `PetscSF`, `PetscViewer`
2568 @*/
2569 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
2570 {
2571   PetscBool ishdf5;
2572 
2573   PetscFunctionBegin;
2574   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2575   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2576   if (!sectiondm) sectiondm = dm;
2577   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2578   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2579   if (globalDofSF) PetscAssertPointer(globalDofSF, 5);
2580   if (localDofSF) PetscAssertPointer(localDofSF, 6);
2581   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2582   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2583   if (ishdf5) {
2584 #if defined(PETSC_HAVE_HDF5)
2585     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2586 #else
2587     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2588 #endif
2589   }
2590   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2591   PetscFunctionReturn(PETSC_SUCCESS);
2592 }
2593 
2594 /*@
2595   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2596 
2597   Collective
2598 
2599   Input Parameters:
2600 + dm        - The `DM` that represents the topology
2601 . viewer    - The `PetscViewer` that represents the on-disk vector data
2602 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2603 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2604 - vec       - The global vector to set values of
2605 
2606   Level: advanced
2607 
2608   Notes:
2609   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.
2610 
2611   Calling sequence:
2612 .vb
2613        DMCreate(PETSC_COMM_WORLD, &dm);
2614        DMSetType(dm, DMPLEX);
2615        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2616        DMPlexTopologyLoad(dm, viewer, &sfX);
2617        DMClone(dm, &sectiondm);
2618        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2619        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2620        DMGetGlobalVector(sectiondm, &vec);
2621        PetscObjectSetName((PetscObject)vec, "vec_name");
2622        DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2623        DMRestoreGlobalVector(sectiondm, &vec);
2624        PetscSFDestroy(&gsf);
2625        PetscSFDestroy(&sfX);
2626        DMDestroy(&sectiondm);
2627        DMDestroy(&dm);
2628 .ve
2629 
2630 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2631           `PetscSF`, `PetscViewer`
2632 @*/
2633 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2634 {
2635   PetscBool ishdf5;
2636 
2637   PetscFunctionBegin;
2638   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2639   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2640   if (!sectiondm) sectiondm = dm;
2641   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2642   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2643   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2644   /* Check consistency */
2645   {
2646     PetscSection section;
2647     PetscBool    includesConstraints;
2648     PetscInt     m, m1;
2649 
2650     PetscCall(VecGetLocalSize(vec, &m1));
2651     PetscCall(DMGetGlobalSection(sectiondm, &section));
2652     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2653     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2654     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2655     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2656   }
2657   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2658   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2659   if (ishdf5) {
2660 #if defined(PETSC_HAVE_HDF5)
2661     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2662 #else
2663     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2664 #endif
2665   }
2666   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2667   PetscFunctionReturn(PETSC_SUCCESS);
2668 }
2669 
2670 /*@
2671   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2672 
2673   Collective
2674 
2675   Input Parameters:
2676 + dm        - The `DM` that represents the topology
2677 . viewer    - The `PetscViewer` that represents the on-disk vector data
2678 . sectiondm - The `DM` that contains the local section on which vec is defined, can be `NULL`
2679 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2680 - vec       - The local vector to set values of
2681 
2682   Level: advanced
2683 
2684   Notes:
2685   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.
2686 
2687   Calling sequence:
2688 .vb
2689        DMCreate(PETSC_COMM_WORLD, &dm);
2690        DMSetType(dm, DMPLEX);
2691        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2692        DMPlexTopologyLoad(dm, viewer, &sfX);
2693        DMClone(dm, &sectiondm);
2694        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2695        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2696        DMGetLocalVector(sectiondm, &vec);
2697        PetscObjectSetName((PetscObject)vec, "vec_name");
2698        DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2699        DMRestoreLocalVector(sectiondm, &vec);
2700        PetscSFDestroy(&lsf);
2701        PetscSFDestroy(&sfX);
2702        DMDestroy(&sectiondm);
2703        DMDestroy(&dm);
2704 .ve
2705 
2706 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2707           `PetscSF`, `PetscViewer`
2708 @*/
2709 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2710 {
2711   PetscBool ishdf5;
2712 
2713   PetscFunctionBegin;
2714   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2715   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2716   if (!sectiondm) sectiondm = dm;
2717   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2718   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2719   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2720   /* Check consistency */
2721   {
2722     PetscSection section;
2723     PetscBool    includesConstraints;
2724     PetscInt     m, m1;
2725 
2726     PetscCall(VecGetLocalSize(vec, &m1));
2727     PetscCall(DMGetLocalSection(sectiondm, &section));
2728     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2729     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2730     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2731     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2732   }
2733   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2734   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2735   if (ishdf5) {
2736 #if defined(PETSC_HAVE_HDF5)
2737     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2738 #else
2739     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2740 #endif
2741   }
2742   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2743   PetscFunctionReturn(PETSC_SUCCESS);
2744 }
2745 
2746 PetscErrorCode DMDestroy_Plex(DM dm)
2747 {
2748   DM_Plex *mesh = (DM_Plex *)dm->data;
2749 
2750   PetscFunctionBegin;
2751   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2752   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2753   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2754   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2755   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerivativeBoundaryValues_C", NULL));
2756   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2757   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2758   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2759   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2760   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2761   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2762   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetDefault_C", NULL));
2763   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetDefault_C", NULL));
2764   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetType_C", NULL));
2765   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetType_C", NULL));
2766   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2767   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2768   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetUseCeed_C", NULL));
2769   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetUseCeed_C", NULL));
2770   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", NULL));
2771   if (--mesh->refct > 0) PetscFunctionReturn(PETSC_SUCCESS);
2772   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2773   PetscCall(PetscFree(mesh->cones));
2774   PetscCall(PetscFree(mesh->coneOrientations));
2775   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2776   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2777   PetscCall(PetscFree(mesh->supports));
2778   PetscCall(PetscFree(mesh->cellTypes));
2779   PetscCall(DMPlexTransformDestroy(&mesh->tr));
2780   PetscCall(PetscFree(mesh->tetgenOpts));
2781   PetscCall(PetscFree(mesh->triangleOpts));
2782   PetscCall(PetscFree(mesh->transformType));
2783   PetscCall(PetscFree(mesh->distributionName));
2784   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2785   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2786   PetscCall(ISDestroy(&mesh->subpointIS));
2787   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2788   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2789   if (mesh->periodic.face_sfs) {
2790     for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(PetscSFDestroy(&mesh->periodic.face_sfs[i]));
2791     PetscCall(PetscFree(mesh->periodic.face_sfs));
2792   }
2793   PetscCall(PetscSFDestroy(&mesh->periodic.composed_sf));
2794   if (mesh->periodic.periodic_points) {
2795     for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(ISDestroy(&mesh->periodic.periodic_points[i]));
2796     PetscCall(PetscFree(mesh->periodic.periodic_points));
2797   }
2798   if (mesh->periodic.transform) PetscCall(PetscFree(mesh->periodic.transform));
2799   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2800   PetscCall(ISDestroy(&mesh->anchorIS));
2801   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2802   PetscCall(PetscFree(mesh->parents));
2803   PetscCall(PetscFree(mesh->childIDs));
2804   PetscCall(PetscSectionDestroy(&mesh->childSection));
2805   PetscCall(PetscFree(mesh->children));
2806   PetscCall(DMDestroy(&mesh->referenceTree));
2807   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2808   PetscCall(PetscFree(mesh->neighbors));
2809   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2810   if (mesh->nonempty_comm != MPI_COMM_NULL && mesh->nonempty_comm != MPI_COMM_SELF) PetscCallMPI(MPI_Comm_free(&mesh->nonempty_comm));
2811   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2812   PetscCall(PetscFree(mesh));
2813   PetscFunctionReturn(PETSC_SUCCESS);
2814 }
2815 
2816 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2817 {
2818   PetscSection           sectionGlobal, sectionLocal;
2819   PetscInt               bs = -1, mbs;
2820   PetscInt               localSize, localStart = 0;
2821   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2822   MatType                mtype;
2823   ISLocalToGlobalMapping ltog;
2824 
2825   PetscFunctionBegin;
2826   PetscCall(MatInitializePackage());
2827   mtype = dm->mattype;
2828   PetscCall(DMGetLocalSection(dm, &sectionLocal));
2829   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2830   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2831   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2832   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2833   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2834   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2835   PetscCall(MatSetType(*J, mtype));
2836   PetscCall(MatSetFromOptions(*J));
2837   PetscCall(MatGetBlockSize(*J, &mbs));
2838   if (mbs > 1) bs = mbs;
2839   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2840   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2841   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2842   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2843   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2844   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2845   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2846   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2847   if (!isShell) {
2848     // There are three states with pblocks, since block starts can have no dofs:
2849     // UNKNOWN) New Block:   An open block has been signalled by pblocks[p] == 1
2850     // TRUE)    Block Start: The first entry in a block has been added
2851     // FALSE)   Block Add:   An additional block entry has been added, since pblocks[p] == 0
2852     PetscBT         blst;
2853     PetscBool3      bstate     = PETSC_BOOL3_UNKNOWN;
2854     PetscBool       fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2855     const PetscInt *perm       = NULL;
2856     PetscInt       *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2857     PetscInt        pStart, pEnd, dof, cdof, num_fields;
2858 
2859     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2860     PetscCall(PetscSectionGetBlockStarts(sectionLocal, &blst));
2861     if (sectionLocal->perm) PetscCall(ISGetIndices(sectionLocal->perm, &perm));
2862 
2863     PetscCall(PetscCalloc1(localSize, &pblocks));
2864     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2865     PetscCall(PetscSectionGetNumFields(sectionGlobal, &num_fields));
2866     // We need to process in the permuted order to get block sizes right
2867     for (PetscInt point = pStart; point < pEnd; ++point) {
2868       const PetscInt p = perm ? perm[point] : point;
2869 
2870       switch (dm->blocking_type) {
2871       case DM_BLOCKING_TOPOLOGICAL_POINT: { // One block per topological point
2872         PetscInt bdof, offset;
2873 
2874         PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2875         PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2876         PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2877         if (blst && PetscBTLookup(blst, p)) bstate = PETSC_BOOL3_UNKNOWN;
2878         if (dof > 0) {
2879           // State change
2880           if (bstate == PETSC_BOOL3_UNKNOWN) bstate = PETSC_BOOL3_TRUE;
2881           else if (bstate == PETSC_BOOL3_TRUE && blst && !PetscBTLookup(blst, p)) bstate = PETSC_BOOL3_FALSE;
2882 
2883           for (PetscInt i = 0; i < dof - cdof; ++i) pblocks[offset - localStart + i] = dof - cdof;
2884           // Signal block concatenation
2885           if (bstate == PETSC_BOOL3_FALSE && dof - cdof) pblocks[offset - localStart] = -(dof - cdof);
2886         }
2887         dof  = dof < 0 ? -(dof + 1) : dof;
2888         bdof = cdof && (dof - cdof) ? 1 : dof;
2889         if (dof) {
2890           if (bs < 0) {
2891             bs = bdof;
2892           } else if (bs != bdof) {
2893             bs = 1;
2894           }
2895         }
2896       } break;
2897       case DM_BLOCKING_FIELD_NODE: {
2898         for (PetscInt field = 0; field < num_fields; field++) {
2899           PetscInt num_comp, bdof, offset;
2900           PetscCall(PetscSectionGetFieldComponents(sectionGlobal, field, &num_comp));
2901           PetscCall(PetscSectionGetFieldDof(sectionGlobal, p, field, &dof));
2902           if (dof < 0) continue;
2903           PetscCall(PetscSectionGetFieldOffset(sectionGlobal, p, field, &offset));
2904           PetscCall(PetscSectionGetFieldConstraintDof(sectionGlobal, p, field, &cdof));
2905           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);
2906           PetscInt num_nodes = dof / num_comp;
2907           for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = (dof - cdof) / num_nodes;
2908           // Handle possibly constant block size (unlikely)
2909           bdof = cdof && (dof - cdof) ? 1 : dof;
2910           if (dof) {
2911             if (bs < 0) {
2912               bs = bdof;
2913             } else if (bs != bdof) {
2914               bs = 1;
2915             }
2916           }
2917         }
2918       } break;
2919       }
2920     }
2921     if (sectionLocal->perm) PetscCall(ISRestoreIndices(sectionLocal->perm, &perm));
2922     /* Must have same blocksize on all procs (some might have no points) */
2923     bsLocal[0] = bs < 0 ? PETSC_INT_MAX : bs;
2924     bsLocal[1] = bs;
2925     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
2926     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2927     else bs = bsMinMax[0];
2928     bs = PetscMax(1, bs);
2929     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
2930     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2931       PetscCall(MatSetBlockSize(*J, bs));
2932       PetscCall(MatSetUp(*J));
2933     } else {
2934       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
2935       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2936       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2937     }
2938     if (pblocks) { // Consolidate blocks
2939       PetscInt nblocks = 0;
2940       pblocks[0]       = PetscAbs(pblocks[0]);
2941       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
2942         if (pblocks[i] == 0) continue;
2943         // Negative block size indicates the blocks should be concatenated
2944         if (pblocks[i] < 0) {
2945           pblocks[i] = -pblocks[i];
2946           pblocks[nblocks - 1] += pblocks[i];
2947         } else {
2948           pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2949         }
2950         for (PetscInt j = 1; j < pblocks[i]; j++)
2951           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);
2952       }
2953       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2954     }
2955     PetscCall(PetscFree(pblocks));
2956   }
2957   PetscCall(MatSetDM(*J, dm));
2958   PetscFunctionReturn(PETSC_SUCCESS);
2959 }
2960 
2961 /*@
2962   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2963 
2964   Not Collective
2965 
2966   Input Parameter:
2967 . dm - The `DMPLEX`
2968 
2969   Output Parameter:
2970 . subsection - The subdomain section
2971 
2972   Level: developer
2973 
2974 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscSection`
2975 @*/
2976 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2977 {
2978   DM_Plex *mesh = (DM_Plex *)dm->data;
2979 
2980   PetscFunctionBegin;
2981   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2982   if (!mesh->subdomainSection) {
2983     PetscSection section;
2984     PetscSF      sf;
2985 
2986     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
2987     PetscCall(DMGetLocalSection(dm, &section));
2988     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
2989     PetscCall(PetscSFDestroy(&sf));
2990   }
2991   *subsection = mesh->subdomainSection;
2992   PetscFunctionReturn(PETSC_SUCCESS);
2993 }
2994 
2995 /*@
2996   DMPlexGetChart - Return the interval for all mesh points [`pStart`, `pEnd`)
2997 
2998   Not Collective
2999 
3000   Input Parameter:
3001 . dm - The `DMPLEX`
3002 
3003   Output Parameters:
3004 + pStart - The first mesh point
3005 - pEnd   - The upper bound for mesh points
3006 
3007   Level: beginner
3008 
3009 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`
3010 @*/
3011 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
3012 {
3013   DM_Plex *mesh = (DM_Plex *)dm->data;
3014 
3015   PetscFunctionBegin;
3016   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3017   if (mesh->tr) PetscCall(DMPlexTransformGetChart(mesh->tr, pStart, pEnd));
3018   else PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
3019   PetscFunctionReturn(PETSC_SUCCESS);
3020 }
3021 
3022 /*@
3023   DMPlexSetChart - Set the interval for all mesh points [`pStart`, `pEnd`)
3024 
3025   Not Collective
3026 
3027   Input Parameters:
3028 + dm     - The `DMPLEX`
3029 . pStart - The first mesh point
3030 - pEnd   - The upper bound for mesh points
3031 
3032   Level: beginner
3033 
3034 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetChart()`
3035 @*/
3036 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
3037 {
3038   DM_Plex *mesh = (DM_Plex *)dm->data;
3039 
3040   PetscFunctionBegin;
3041   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3042   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
3043   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
3044   PetscCall(PetscFree(mesh->cellTypes));
3045   PetscFunctionReturn(PETSC_SUCCESS);
3046 }
3047 
3048 /*@
3049   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
3050 
3051   Not Collective
3052 
3053   Input Parameters:
3054 + dm - The `DMPLEX`
3055 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3056 
3057   Output Parameter:
3058 . size - The cone size for point `p`
3059 
3060   Level: beginner
3061 
3062 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3063 @*/
3064 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
3065 {
3066   DM_Plex *mesh = (DM_Plex *)dm->data;
3067 
3068   PetscFunctionBegin;
3069   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3070   PetscAssertPointer(size, 3);
3071   if (mesh->tr) PetscCall(DMPlexTransformGetConeSize(mesh->tr, p, size));
3072   else PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
3073   PetscFunctionReturn(PETSC_SUCCESS);
3074 }
3075 
3076 /*@
3077   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
3078 
3079   Not Collective
3080 
3081   Input Parameters:
3082 + dm   - The `DMPLEX`
3083 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3084 - size - The cone size for point `p`
3085 
3086   Level: beginner
3087 
3088   Note:
3089   This should be called after `DMPlexSetChart()`.
3090 
3091 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
3092 @*/
3093 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
3094 {
3095   DM_Plex *mesh = (DM_Plex *)dm->data;
3096 
3097   PetscFunctionBegin;
3098   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3099   PetscCheck(!mesh->tr, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Cannot call DMPlexSetConeSize() on a mesh with a transform defined.");
3100   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
3101   PetscFunctionReturn(PETSC_SUCCESS);
3102 }
3103 
3104 /*@C
3105   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
3106 
3107   Not Collective
3108 
3109   Input Parameters:
3110 + dm - The `DMPLEX`
3111 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3112 
3113   Output Parameter:
3114 . cone - An array of points which are on the in-edges for point `p`, the length of `cone` is the result of `DMPlexGetConeSize()`
3115 
3116   Level: beginner
3117 
3118   Fortran Notes:
3119   `cone` must be declared with
3120 .vb
3121   PetscInt, pointer :: cone(:)
3122 .ve
3123 
3124   You must also call `DMPlexRestoreCone()` after you finish using the array.
3125   `DMPlexRestoreCone()` is not needed/available in C.
3126 
3127 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`, `DMPlexRestoreCone()`
3128 @*/
3129 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
3130 {
3131   DM_Plex *mesh = (DM_Plex *)dm->data;
3132   PetscInt off;
3133 
3134   PetscFunctionBegin;
3135   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3136   PetscAssertPointer(cone, 3);
3137   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3138   *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3139   PetscFunctionReturn(PETSC_SUCCESS);
3140 }
3141 
3142 /*@
3143   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
3144 
3145   Not Collective
3146 
3147   Input Parameters:
3148 + dm - The `DMPLEX`
3149 - p  - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3150 
3151   Output Parameters:
3152 + pConesSection - `PetscSection` describing the layout of `pCones`
3153 - pCones        - An `IS` containing the points which are on the in-edges for the point set `p`
3154 
3155   Level: intermediate
3156 
3157 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`, `PetscSection`, `IS`
3158 @*/
3159 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
3160 {
3161   PetscSection cs, newcs;
3162   PetscInt    *cones;
3163   PetscInt    *newarr = NULL;
3164   PetscInt     n;
3165 
3166   PetscFunctionBegin;
3167   PetscCall(DMPlexGetCones(dm, &cones));
3168   PetscCall(DMPlexGetConeSection(dm, &cs));
3169   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
3170   if (pConesSection) *pConesSection = newcs;
3171   if (pCones) {
3172     PetscCall(PetscSectionGetStorageSize(newcs, &n));
3173     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
3174   }
3175   PetscFunctionReturn(PETSC_SUCCESS);
3176 }
3177 
3178 /*@
3179   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
3180 
3181   Not Collective
3182 
3183   Input Parameters:
3184 + dm     - The `DMPLEX`
3185 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3186 
3187   Output Parameter:
3188 . expandedPoints - An `IS` containing the of vertices recursively expanded from input points
3189 
3190   Level: advanced
3191 
3192   Notes:
3193   Like `DMPlexGetConeRecursive()` but returns only the 0-depth `IS` (i.e. vertices only) and no sections.
3194 
3195   There is no corresponding Restore function, just call `ISDestroy()` on the returned `IS` to deallocate.
3196 
3197 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`,
3198           `DMPlexGetDepth()`, `IS`
3199 @*/
3200 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
3201 {
3202   IS      *expandedPointsAll;
3203   PetscInt depth;
3204 
3205   PetscFunctionBegin;
3206   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3207   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3208   PetscAssertPointer(expandedPoints, 3);
3209   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3210   *expandedPoints = expandedPointsAll[0];
3211   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
3212   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3213   PetscFunctionReturn(PETSC_SUCCESS);
3214 }
3215 
3216 /*@
3217   DMPlexGetConeRecursive - Expand each given point into its cone points and do that recursively until we end up just with vertices
3218   (DAG points of depth 0, i.e., without cones).
3219 
3220   Not Collective
3221 
3222   Input Parameters:
3223 + dm     - The `DMPLEX`
3224 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3225 
3226   Output Parameters:
3227 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3228 . expandedPoints - (optional) An array of index sets with recursively expanded cones
3229 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3230 
3231   Level: advanced
3232 
3233   Notes:
3234   Like `DMPlexGetConeTuple()` but recursive.
3235 
3236   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.
3237   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
3238 
3239   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\:
3240   (1) DAG points in `expandedPoints`[d+1] with `depth` d+1 to their cone points in `expandedPoints`[d];
3241   (2) DAG points in `expandedPoints`[d+1] with `depth` in [0,d] to the same points in `expandedPoints`[d].
3242 
3243 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3244           `DMPlexGetDepth()`, `PetscSection`, `IS`
3245 @*/
3246 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3247 {
3248   const PetscInt *arr0 = NULL, *cone = NULL;
3249   PetscInt       *arr = NULL, *newarr = NULL;
3250   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
3251   IS             *expandedPoints_;
3252   PetscSection   *sections_;
3253 
3254   PetscFunctionBegin;
3255   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3256   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3257   if (depth) PetscAssertPointer(depth, 3);
3258   if (expandedPoints) PetscAssertPointer(expandedPoints, 4);
3259   if (sections) PetscAssertPointer(sections, 5);
3260   PetscCall(ISGetLocalSize(points, &n));
3261   PetscCall(ISGetIndices(points, &arr0));
3262   PetscCall(DMPlexGetDepth(dm, &depth_));
3263   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
3264   PetscCall(PetscCalloc1(depth_, &sections_));
3265   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
3266   for (d = depth_ - 1; d >= 0; d--) {
3267     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
3268     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
3269     for (i = 0; i < n; i++) {
3270       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
3271       if (arr[i] >= start && arr[i] < end) {
3272         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
3273         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
3274       } else {
3275         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
3276       }
3277     }
3278     PetscCall(PetscSectionSetUp(sections_[d]));
3279     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
3280     PetscCall(PetscMalloc1(newn, &newarr));
3281     for (i = 0; i < n; i++) {
3282       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
3283       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
3284       if (cn > 1) {
3285         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
3286         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
3287       } else {
3288         newarr[co] = arr[i];
3289       }
3290     }
3291     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
3292     arr = newarr;
3293     n   = newn;
3294   }
3295   PetscCall(ISRestoreIndices(points, &arr0));
3296   *depth = depth_;
3297   if (expandedPoints) *expandedPoints = expandedPoints_;
3298   else {
3299     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
3300     PetscCall(PetscFree(expandedPoints_));
3301   }
3302   if (sections) *sections = sections_;
3303   else {
3304     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
3305     PetscCall(PetscFree(sections_));
3306   }
3307   PetscFunctionReturn(PETSC_SUCCESS);
3308 }
3309 
3310 /*@
3311   DMPlexRestoreConeRecursive - Deallocates arrays created by `DMPlexGetConeRecursive()`
3312 
3313   Not Collective
3314 
3315   Input Parameters:
3316 + dm     - The `DMPLEX`
3317 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3318 
3319   Output Parameters:
3320 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3321 . expandedPoints - (optional) An array of recursively expanded cones
3322 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3323 
3324   Level: advanced
3325 
3326   Note:
3327   See `DMPlexGetConeRecursive()`
3328 
3329 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3330           `DMPlexGetDepth()`, `IS`, `PetscSection`
3331 @*/
3332 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3333 {
3334   PetscInt d, depth_;
3335 
3336   PetscFunctionBegin;
3337   PetscCall(DMPlexGetDepth(dm, &depth_));
3338   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
3339   if (depth) *depth = 0;
3340   if (expandedPoints) {
3341     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&(*expandedPoints)[d]));
3342     PetscCall(PetscFree(*expandedPoints));
3343   }
3344   if (sections) {
3345     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&(*sections)[d]));
3346     PetscCall(PetscFree(*sections));
3347   }
3348   PetscFunctionReturn(PETSC_SUCCESS);
3349 }
3350 
3351 /*@
3352   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
3353 
3354   Not Collective
3355 
3356   Input Parameters:
3357 + dm   - The `DMPLEX`
3358 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3359 - cone - An array of points which are on the in-edges for point `p`, its length must have been previously provided with `DMPlexSetConeSize()`
3360 
3361   Level: beginner
3362 
3363   Note:
3364   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3365 
3366 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3367 @*/
3368 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3369 {
3370   DM_Plex *mesh = (DM_Plex *)dm->data;
3371   PetscInt dof, off, c;
3372 
3373   PetscFunctionBegin;
3374   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3375   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3376   if (dof) PetscAssertPointer(cone, 3);
3377   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3378   if (PetscDefined(USE_DEBUG)) {
3379     PetscInt pStart, pEnd;
3380     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3381     PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3382     for (c = 0; c < dof; ++c) {
3383       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);
3384       mesh->cones[off + c] = cone[c];
3385     }
3386   } else {
3387     for (c = 0; c < dof; ++c) mesh->cones[off + c] = cone[c];
3388   }
3389   PetscFunctionReturn(PETSC_SUCCESS);
3390 }
3391 
3392 /*@C
3393   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3394 
3395   Not Collective
3396 
3397   Input Parameters:
3398 + dm - The `DMPLEX`
3399 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3400 
3401   Output Parameter:
3402 . coneOrientation - An array of orientations which are on the in-edges for point `p`. An orientation is an
3403                     integer giving the prescription for cone traversal. Its length is given by the result of `DMPlexSetConeSize()`
3404 
3405   Level: beginner
3406 
3407   Note:
3408   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3409   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3410   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3411   with the identity.
3412 
3413   Fortran Notes:
3414   You must call `DMPlexRestoreConeOrientation()` after you finish using the returned array.
3415   `DMPlexRestoreConeOrientation()` is not needed/available in C.
3416 
3417 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetConeSize()`, `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`,
3418           `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3419 @*/
3420 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3421 {
3422   DM_Plex *mesh = (DM_Plex *)dm->data;
3423   PetscInt off;
3424 
3425   PetscFunctionBegin;
3426   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3427   if (PetscDefined(USE_DEBUG)) {
3428     PetscInt dof;
3429     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3430     if (dof) PetscAssertPointer(coneOrientation, 3);
3431   }
3432   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3433 
3434   *coneOrientation = &mesh->coneOrientations[off];
3435   PetscFunctionReturn(PETSC_SUCCESS);
3436 }
3437 
3438 /*@
3439   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3440 
3441   Not Collective
3442 
3443   Input Parameters:
3444 + dm              - The `DMPLEX`
3445 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3446 - coneOrientation - An array of orientations. Its length is given by the result of `DMPlexSetConeSize()`
3447 
3448   Level: beginner
3449 
3450   Notes:
3451   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3452 
3453   The meaning of coneOrientation is detailed in `DMPlexGetConeOrientation()`.
3454 
3455 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3456 @*/
3457 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3458 {
3459   DM_Plex *mesh = (DM_Plex *)dm->data;
3460   PetscInt pStart, pEnd;
3461   PetscInt dof, off, c;
3462 
3463   PetscFunctionBegin;
3464   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3465   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3466   if (dof) PetscAssertPointer(coneOrientation, 3);
3467   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3468   if (PetscDefined(USE_DEBUG)) {
3469     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3470     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);
3471     for (c = 0; c < dof; ++c) {
3472       PetscInt cdof, o = coneOrientation[c];
3473 
3474       PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3475       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);
3476       mesh->coneOrientations[off + c] = o;
3477     }
3478   } else {
3479     for (c = 0; c < dof; ++c) mesh->coneOrientations[off + c] = coneOrientation[c];
3480   }
3481   PetscFunctionReturn(PETSC_SUCCESS);
3482 }
3483 
3484 /*@
3485   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3486 
3487   Not Collective
3488 
3489   Input Parameters:
3490 + dm        - The `DMPLEX`
3491 . p         - The point, which must lie in the chart set with `DMPlexSetChart()`
3492 . conePos   - The local index in the cone where the point should be put
3493 - conePoint - The mesh point to insert
3494 
3495   Level: beginner
3496 
3497 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3498 @*/
3499 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3500 {
3501   DM_Plex *mesh = (DM_Plex *)dm->data;
3502   PetscInt pStart, pEnd;
3503   PetscInt dof, off;
3504 
3505   PetscFunctionBegin;
3506   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3507   if (PetscDefined(USE_DEBUG)) {
3508     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3509     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);
3510     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);
3511     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3512     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);
3513   }
3514   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3515   mesh->cones[off + conePos] = conePoint;
3516   PetscFunctionReturn(PETSC_SUCCESS);
3517 }
3518 
3519 /*@
3520   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3521 
3522   Not Collective
3523 
3524   Input Parameters:
3525 + dm              - The `DMPLEX`
3526 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3527 . conePos         - The local index in the cone where the point should be put
3528 - coneOrientation - The point orientation to insert
3529 
3530   Level: beginner
3531 
3532   Note:
3533   The meaning of coneOrientation values is detailed in `DMPlexGetConeOrientation()`.
3534 
3535 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3536 @*/
3537 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3538 {
3539   DM_Plex *mesh = (DM_Plex *)dm->data;
3540   PetscInt pStart, pEnd;
3541   PetscInt dof, off;
3542 
3543   PetscFunctionBegin;
3544   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3545   if (PetscDefined(USE_DEBUG)) {
3546     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3547     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);
3548     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3549     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);
3550   }
3551   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3552   mesh->coneOrientations[off + conePos] = coneOrientation;
3553   PetscFunctionReturn(PETSC_SUCCESS);
3554 }
3555 
3556 /*@C
3557   DMPlexGetOrientedCone - Return the points and orientations on the in-edges for this point in the DAG
3558 
3559   Not collective
3560 
3561   Input Parameters:
3562 + dm - The DMPlex
3563 - p  - The point, which must lie in the chart set with DMPlexSetChart()
3564 
3565   Output Parameters:
3566 + cone - An array of points which are on the in-edges for point `p`
3567 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3568          integer giving the prescription for cone traversal.
3569 
3570   Level: beginner
3571 
3572   Notes:
3573   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3574   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3575   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3576   with the identity.
3577 
3578   You must also call `DMPlexRestoreOrientedCone()` after you finish using the returned array.
3579 
3580   Fortran Notes:
3581   `cone` and `ornt` must be declared with
3582 .vb
3583   PetscInt, pointer :: cone(:)
3584   PetscInt, pointer :: ornt(:)
3585 .ve
3586 
3587 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3588 @*/
3589 PetscErrorCode DMPlexGetOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3590 {
3591   DM_Plex *mesh = (DM_Plex *)dm->data;
3592 
3593   PetscFunctionBegin;
3594   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3595   if (mesh->tr) {
3596     PetscCall(DMPlexTransformGetCone(mesh->tr, p, cone, ornt));
3597   } else {
3598     PetscInt off;
3599     if (PetscDefined(USE_DEBUG)) {
3600       PetscInt dof;
3601       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3602       if (dof) {
3603         if (cone) PetscAssertPointer(cone, 3);
3604         if (ornt) PetscAssertPointer(ornt, 4);
3605       }
3606     }
3607     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3608     if (cone) *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3609     if (ornt) *ornt = PetscSafePointerPlusOffset(mesh->coneOrientations, off);
3610   }
3611   PetscFunctionReturn(PETSC_SUCCESS);
3612 }
3613 
3614 /*@C
3615   DMPlexRestoreOrientedCone - Restore the points and orientations on the in-edges for this point in the DAG obtained with `DMPlexGetOrientedCone()`
3616 
3617   Not Collective
3618 
3619   Input Parameters:
3620 + dm   - The DMPlex
3621 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3622 . cone - An array of points which are on the in-edges for point p
3623 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3624          integer giving the prescription for cone traversal.
3625 
3626   Level: beginner
3627 
3628 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3629 @*/
3630 PetscErrorCode DMPlexRestoreOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3631 {
3632   DM_Plex *mesh = (DM_Plex *)dm->data;
3633 
3634   PetscFunctionBegin;
3635   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3636   if (mesh->tr) PetscCall(DMPlexTransformRestoreCone(mesh->tr, p, cone, ornt));
3637   PetscFunctionReturn(PETSC_SUCCESS);
3638 }
3639 
3640 /*@
3641   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3642 
3643   Not Collective
3644 
3645   Input Parameters:
3646 + dm - The `DMPLEX`
3647 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3648 
3649   Output Parameter:
3650 . size - The support size for point `p`
3651 
3652   Level: beginner
3653 
3654 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3655 @*/
3656 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3657 {
3658   DM_Plex *mesh = (DM_Plex *)dm->data;
3659 
3660   PetscFunctionBegin;
3661   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3662   PetscAssertPointer(size, 3);
3663   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3664   PetscFunctionReturn(PETSC_SUCCESS);
3665 }
3666 
3667 /*@
3668   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3669 
3670   Not Collective
3671 
3672   Input Parameters:
3673 + dm   - The `DMPLEX`
3674 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3675 - size - The support size for point `p`
3676 
3677   Level: beginner
3678 
3679   Note:
3680   This should be called after `DMPlexSetChart()`.
3681 
3682 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3683 @*/
3684 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3685 {
3686   DM_Plex *mesh = (DM_Plex *)dm->data;
3687 
3688   PetscFunctionBegin;
3689   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3690   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3691   PetscFunctionReturn(PETSC_SUCCESS);
3692 }
3693 
3694 /*@C
3695   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3696 
3697   Not Collective
3698 
3699   Input Parameters:
3700 + dm - The `DMPLEX`
3701 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3702 
3703   Output Parameter:
3704 . support - An array of points which are on the out-edges for point `p`, its length is that obtained from `DMPlexGetSupportSize()`
3705 
3706   Level: beginner
3707 
3708   Fortran Notes:
3709   `support` must be declared with
3710 .vb
3711   PetscInt, pointer :: support(:)
3712 .ve
3713 
3714   You must also call `DMPlexRestoreSupport()` after you finish using the returned array.
3715   `DMPlexRestoreSupport()` is not needed/available in C.
3716 
3717 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3718 @*/
3719 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3720 {
3721   DM_Plex *mesh = (DM_Plex *)dm->data;
3722   PetscInt off;
3723 
3724   PetscFunctionBegin;
3725   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3726   PetscAssertPointer(support, 3);
3727   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3728   *support = PetscSafePointerPlusOffset(mesh->supports, off);
3729   PetscFunctionReturn(PETSC_SUCCESS);
3730 }
3731 
3732 /*@
3733   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3734 
3735   Not Collective
3736 
3737   Input Parameters:
3738 + dm      - The `DMPLEX`
3739 . p       - The point, which must lie in the chart set with `DMPlexSetChart()`
3740 - support - An array of points which are on the out-edges for point `p`, its length is that obtained from `DMPlexGetSupportSize()`
3741 
3742   Level: beginner
3743 
3744   Note:
3745   This should be called after all calls to `DMPlexSetSupportSize()` and `DMSetUp()`.
3746 
3747 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3748 @*/
3749 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3750 {
3751   DM_Plex *mesh = (DM_Plex *)dm->data;
3752   PetscInt pStart, pEnd;
3753   PetscInt dof, off, c;
3754 
3755   PetscFunctionBegin;
3756   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3757   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3758   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3759   if (dof) PetscAssertPointer(support, 3);
3760   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3761   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);
3762   for (c = 0; c < dof; ++c) {
3763     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);
3764     mesh->supports[off + c] = support[c];
3765   }
3766   PetscFunctionReturn(PETSC_SUCCESS);
3767 }
3768 
3769 /*@
3770   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3771 
3772   Not Collective
3773 
3774   Input Parameters:
3775 + dm           - The `DMPLEX`
3776 . p            - The point, which must lie in the chart set with `DMPlexSetChart()`
3777 . supportPos   - The local index in the cone where the point should be put
3778 - supportPoint - The mesh point to insert
3779 
3780   Level: beginner
3781 
3782 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3783 @*/
3784 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3785 {
3786   DM_Plex *mesh = (DM_Plex *)dm->data;
3787   PetscInt pStart, pEnd;
3788   PetscInt dof, off;
3789 
3790   PetscFunctionBegin;
3791   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3792   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3793   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3794   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3795   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);
3796   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);
3797   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);
3798   mesh->supports[off + supportPos] = supportPoint;
3799   PetscFunctionReturn(PETSC_SUCCESS);
3800 }
3801 
3802 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3803 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3804 {
3805   switch (ct) {
3806   case DM_POLYTOPE_SEGMENT:
3807     if (o == -1) return -2;
3808     break;
3809   case DM_POLYTOPE_TRIANGLE:
3810     if (o == -3) return -1;
3811     if (o == -2) return -3;
3812     if (o == -1) return -2;
3813     break;
3814   case DM_POLYTOPE_QUADRILATERAL:
3815     if (o == -4) return -2;
3816     if (o == -3) return -1;
3817     if (o == -2) return -4;
3818     if (o == -1) return -3;
3819     break;
3820   default:
3821     return o;
3822   }
3823   return o;
3824 }
3825 
3826 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3827 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3828 {
3829   switch (ct) {
3830   case DM_POLYTOPE_SEGMENT:
3831     if ((o == -2) || (o == 1)) return -1;
3832     if (o == -1) return 0;
3833     break;
3834   case DM_POLYTOPE_TRIANGLE:
3835     if (o == -3) return -2;
3836     if (o == -2) return -1;
3837     if (o == -1) return -3;
3838     break;
3839   case DM_POLYTOPE_QUADRILATERAL:
3840     if (o == -4) return -2;
3841     if (o == -3) return -1;
3842     if (o == -2) return -4;
3843     if (o == -1) return -3;
3844     break;
3845   default:
3846     return o;
3847   }
3848   return o;
3849 }
3850 
3851 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3852 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3853 {
3854   PetscInt pStart, pEnd, p;
3855 
3856   PetscFunctionBegin;
3857   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3858   for (p = pStart; p < pEnd; ++p) {
3859     const PetscInt *cone, *ornt;
3860     PetscInt        coneSize, c;
3861 
3862     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3863     PetscCall(DMPlexGetCone(dm, p, &cone));
3864     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3865     for (c = 0; c < coneSize; ++c) {
3866       DMPolytopeType ct;
3867       const PetscInt o = ornt[c];
3868 
3869       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3870       switch (ct) {
3871       case DM_POLYTOPE_SEGMENT:
3872         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3873         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3874         break;
3875       case DM_POLYTOPE_TRIANGLE:
3876         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3877         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3878         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3879         break;
3880       case DM_POLYTOPE_QUADRILATERAL:
3881         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3882         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3883         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3884         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3885         break;
3886       default:
3887         break;
3888       }
3889     }
3890   }
3891   PetscFunctionReturn(PETSC_SUCCESS);
3892 }
3893 
3894 static inline PetscErrorCode DMPlexGetTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3895 {
3896   DM_Plex *mesh = (DM_Plex *)dm->data;
3897 
3898   PetscFunctionBeginHot;
3899   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3900     if (useCone) {
3901       PetscCall(DMPlexGetConeSize(dm, p, size));
3902       PetscCall(DMPlexGetOrientedCone(dm, p, arr, ornt));
3903     } else {
3904       PetscCall(DMPlexGetSupportSize(dm, p, size));
3905       PetscCall(DMPlexGetSupport(dm, p, arr));
3906     }
3907   } else {
3908     if (useCone) {
3909       const PetscSection s   = mesh->coneSection;
3910       const PetscInt     ps  = p - s->pStart;
3911       const PetscInt     off = s->atlasOff[ps];
3912 
3913       *size = s->atlasDof[ps];
3914       *arr  = mesh->cones + off;
3915       *ornt = mesh->coneOrientations + off;
3916     } else {
3917       const PetscSection s   = mesh->supportSection;
3918       const PetscInt     ps  = p - s->pStart;
3919       const PetscInt     off = s->atlasOff[ps];
3920 
3921       *size = s->atlasDof[ps];
3922       *arr  = mesh->supports + off;
3923     }
3924   }
3925   PetscFunctionReturn(PETSC_SUCCESS);
3926 }
3927 
3928 static inline PetscErrorCode DMPlexRestoreTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3929 {
3930   DM_Plex *mesh = (DM_Plex *)dm->data;
3931 
3932   PetscFunctionBeginHot;
3933   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3934     if (useCone) PetscCall(DMPlexRestoreOrientedCone(dm, p, arr, ornt));
3935   }
3936   PetscFunctionReturn(PETSC_SUCCESS);
3937 }
3938 
3939 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3940 {
3941   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3942   PetscInt       *closure;
3943   const PetscInt *tmp = NULL, *tmpO = NULL;
3944   PetscInt        off = 0, tmpSize, t;
3945 
3946   PetscFunctionBeginHot;
3947   if (ornt) {
3948     PetscCall(DMPlexGetCellType(dm, p, &ct));
3949     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;
3950   }
3951   if (*points) {
3952     closure = *points;
3953   } else {
3954     PetscInt maxConeSize, maxSupportSize;
3955     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3956     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
3957   }
3958   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3959   if (ct == DM_POLYTOPE_UNKNOWN) {
3960     closure[off++] = p;
3961     closure[off++] = 0;
3962     for (t = 0; t < tmpSize; ++t) {
3963       closure[off++] = tmp[t];
3964       closure[off++] = tmpO ? tmpO[t] : 0;
3965     }
3966   } else {
3967     const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, ornt);
3968 
3969     /* We assume that cells with a valid type have faces with a valid type */
3970     closure[off++] = p;
3971     closure[off++] = ornt;
3972     for (t = 0; t < tmpSize; ++t) {
3973       DMPolytopeType ft;
3974 
3975       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3976       closure[off++] = tmp[arr[t]];
3977       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3978     }
3979   }
3980   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3981   if (numPoints) *numPoints = tmpSize + 1;
3982   if (points) *points = closure;
3983   PetscFunctionReturn(PETSC_SUCCESS);
3984 }
3985 
3986 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
3987 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3988 {
3989   const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, o);
3990   const PetscInt *cone, *ornt;
3991   PetscInt       *pts, *closure = NULL;
3992   DMPolytopeType  ft;
3993   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3994   PetscInt        dim, coneSize, c, d, clSize, cl;
3995 
3996   PetscFunctionBeginHot;
3997   PetscCall(DMGetDimension(dm, &dim));
3998   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3999   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
4000   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
4001   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
4002   maxSize       = PetscMax(coneSeries, supportSeries);
4003   if (*points) {
4004     pts = *points;
4005   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
4006   c        = 0;
4007   pts[c++] = point;
4008   pts[c++] = o;
4009   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
4010   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
4011   for (cl = 0; cl < clSize * 2; cl += 2) {
4012     pts[c++] = closure[cl];
4013     pts[c++] = closure[cl + 1];
4014   }
4015   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
4016   for (cl = 0; cl < clSize * 2; cl += 2) {
4017     pts[c++] = closure[cl];
4018     pts[c++] = closure[cl + 1];
4019   }
4020   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
4021   for (d = 2; d < coneSize; ++d) {
4022     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
4023     pts[c++] = cone[arr[d * 2 + 0]];
4024     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
4025   }
4026   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
4027   if (dim >= 3) {
4028     for (d = 2; d < coneSize; ++d) {
4029       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
4030       const PetscInt *fcone, *fornt;
4031       PetscInt        fconeSize, fc, i;
4032 
4033       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
4034       const PetscInt *farr = DMPolytopeTypeGetArrangement(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
4035       PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
4036       for (fc = 0; fc < fconeSize; ++fc) {
4037         const PetscInt cp = fcone[farr[fc * 2 + 0]];
4038         const PetscInt co = farr[fc * 2 + 1];
4039 
4040         for (i = 0; i < c; i += 2)
4041           if (pts[i] == cp) break;
4042         if (i == c) {
4043           PetscCall(DMPlexGetCellType(dm, cp, &ft));
4044           pts[c++] = cp;
4045           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
4046         }
4047       }
4048       PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
4049     }
4050   }
4051   *numPoints = c / 2;
4052   *points    = pts;
4053   PetscFunctionReturn(PETSC_SUCCESS);
4054 }
4055 
4056 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4057 {
4058   DMPolytopeType ct;
4059   PetscInt      *closure, *fifo;
4060   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
4061   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
4062   PetscInt       depth, maxSize;
4063 
4064   PetscFunctionBeginHot;
4065   PetscCall(DMPlexGetDepth(dm, &depth));
4066   if (depth == 1) {
4067     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
4068     PetscFunctionReturn(PETSC_SUCCESS);
4069   }
4070   PetscCall(DMPlexGetCellType(dm, p, &ct));
4071   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;
4072   if (DMPolytopeTypeIsHybrid(ct) && ct != DM_POLYTOPE_POINT_PRISM_TENSOR) {
4073     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
4074     PetscFunctionReturn(PETSC_SUCCESS);
4075   }
4076   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
4077   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
4078   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
4079   maxSize       = PetscMax(coneSeries, supportSeries);
4080   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
4081   if (*points) {
4082     closure = *points;
4083   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
4084   closure[closureSize++] = p;
4085   closure[closureSize++] = ornt;
4086   fifo[fifoSize++]       = p;
4087   fifo[fifoSize++]       = ornt;
4088   fifo[fifoSize++]       = ct;
4089   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
4090   while (fifoSize - fifoStart) {
4091     const PetscInt       q    = fifo[fifoStart++];
4092     const PetscInt       o    = fifo[fifoStart++];
4093     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
4094     const PetscInt      *qarr = DMPolytopeTypeGetArrangement(qt, o);
4095     const PetscInt      *tmp, *tmpO = NULL;
4096     PetscInt             tmpSize, t;
4097 
4098     if (PetscDefined(USE_DEBUG)) {
4099       PetscInt nO = DMPolytopeTypeGetNumArrangements(qt) / 2;
4100       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);
4101     }
4102     PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4103     for (t = 0; t < tmpSize; ++t) {
4104       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
4105       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
4106       const PetscInt cp = tmp[ip];
4107       PetscCall(DMPlexGetCellType(dm, cp, &ct));
4108       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
4109       PetscInt       c;
4110 
4111       /* Check for duplicate */
4112       for (c = 0; c < closureSize; c += 2) {
4113         if (closure[c] == cp) break;
4114       }
4115       if (c == closureSize) {
4116         closure[closureSize++] = cp;
4117         closure[closureSize++] = co;
4118         fifo[fifoSize++]       = cp;
4119         fifo[fifoSize++]       = co;
4120         fifo[fifoSize++]       = ct;
4121       }
4122     }
4123     PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4124   }
4125   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
4126   if (numPoints) *numPoints = closureSize / 2;
4127   if (points) *points = closure;
4128   PetscFunctionReturn(PETSC_SUCCESS);
4129 }
4130 
4131 /*@C
4132   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
4133 
4134   Not Collective
4135 
4136   Input Parameters:
4137 + dm      - The `DMPLEX`
4138 . p       - The mesh point
4139 - useCone - `PETSC_TRUE` for the closure, otherwise return the star
4140 
4141   Input/Output Parameter:
4142 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
4143            if *points is `NULL` on input, internal storage will be returned, use `DMPlexRestoreTransitiveClosure()`,
4144            otherwise the provided array is used to hold the values
4145 
4146   Output Parameter:
4147 . numPoints - The number of points in the closure, so `points` is of size 2*`numPoints`
4148 
4149   Level: beginner
4150 
4151   Note:
4152   If using internal storage (points is `NULL` on input), each call overwrites the last output.
4153 
4154   Fortran Notes:
4155   `points` must be declared with
4156 .vb
4157   PetscInt, pointer :: points(:)
4158 .ve
4159   and is always allocated by the function.
4160 
4161   The `numPoints` argument is not present in the Fortran binding.
4162 
4163 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4164 @*/
4165 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4166 {
4167   PetscFunctionBeginHot;
4168   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4169   if (numPoints) PetscAssertPointer(numPoints, 4);
4170   if (points) PetscAssertPointer(points, 5);
4171   if (PetscDefined(USE_DEBUG)) {
4172     PetscInt pStart, pEnd;
4173     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4174     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);
4175   }
4176   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
4177   PetscFunctionReturn(PETSC_SUCCESS);
4178 }
4179 
4180 /*@C
4181   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
4182 
4183   Not Collective
4184 
4185   Input Parameters:
4186 + dm        - The `DMPLEX`
4187 . p         - The mesh point
4188 . useCone   - `PETSC_TRUE` for the closure, otherwise return the star
4189 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
4190 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
4191 
4192   Level: beginner
4193 
4194   Note:
4195   If not using internal storage (points is not `NULL` on input), this call is unnecessary
4196 
4197 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4198 @*/
4199 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4200 {
4201   PetscFunctionBeginHot;
4202   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4203   if (numPoints) *numPoints = 0;
4204   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
4205   PetscFunctionReturn(PETSC_SUCCESS);
4206 }
4207 
4208 /*@
4209   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
4210 
4211   Not Collective
4212 
4213   Input Parameter:
4214 . dm - The `DMPLEX`
4215 
4216   Output Parameters:
4217 + maxConeSize    - The maximum number of in-edges
4218 - maxSupportSize - The maximum number of out-edges
4219 
4220   Level: beginner
4221 
4222 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
4223 @*/
4224 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
4225 {
4226   DM_Plex *mesh = (DM_Plex *)dm->data;
4227 
4228   PetscFunctionBegin;
4229   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4230   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
4231   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
4232   PetscFunctionReturn(PETSC_SUCCESS);
4233 }
4234 
4235 PetscErrorCode DMSetUp_Plex(DM dm)
4236 {
4237   DM_Plex *mesh = (DM_Plex *)dm->data;
4238   PetscInt size, maxSupportSize;
4239 
4240   PetscFunctionBegin;
4241   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4242   PetscCall(PetscSectionSetUp(mesh->coneSection));
4243   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
4244   PetscCall(PetscMalloc1(size, &mesh->cones));
4245   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
4246   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4247   if (maxSupportSize) {
4248     PetscCall(PetscSectionSetUp(mesh->supportSection));
4249     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
4250     PetscCall(PetscMalloc1(size, &mesh->supports));
4251   }
4252   PetscFunctionReturn(PETSC_SUCCESS);
4253 }
4254 
4255 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
4256 {
4257   PetscFunctionBegin;
4258   if (subdm) PetscCall(DMClone(dm, subdm));
4259   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, NULL, NULL, is, subdm));
4260   if (subdm) (*subdm)->useNatural = dm->useNatural;
4261   if (dm->useNatural && dm->sfMigration) {
4262     PetscSF sfNatural;
4263 
4264     (*subdm)->sfMigration = dm->sfMigration;
4265     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
4266     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
4267     (*subdm)->sfNatural = sfNatural;
4268   }
4269   PetscFunctionReturn(PETSC_SUCCESS);
4270 }
4271 
4272 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
4273 {
4274   PetscInt i = 0;
4275 
4276   PetscFunctionBegin;
4277   PetscCall(DMClone(dms[0], superdm));
4278   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
4279   (*superdm)->useNatural = PETSC_FALSE;
4280   for (i = 0; i < len; i++) {
4281     if (dms[i]->useNatural && dms[i]->sfMigration) {
4282       PetscSF sfNatural;
4283 
4284       (*superdm)->sfMigration = dms[i]->sfMigration;
4285       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
4286       (*superdm)->useNatural = PETSC_TRUE;
4287       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
4288       (*superdm)->sfNatural = sfNatural;
4289       break;
4290     }
4291   }
4292   PetscFunctionReturn(PETSC_SUCCESS);
4293 }
4294 
4295 /*@
4296   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
4297 
4298   Not Collective
4299 
4300   Input Parameter:
4301 . dm - The `DMPLEX`
4302 
4303   Level: beginner
4304 
4305   Note:
4306   This should be called after all calls to `DMPlexSetCone()`
4307 
4308 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
4309 @*/
4310 PetscErrorCode DMPlexSymmetrize(DM dm)
4311 {
4312   DM_Plex  *mesh = (DM_Plex *)dm->data;
4313   PetscInt *offsets;
4314   PetscInt  supportSize;
4315   PetscInt  pStart, pEnd, p;
4316 
4317   PetscFunctionBegin;
4318   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4319   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
4320   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
4321   /* Calculate support sizes */
4322   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4323   for (p = pStart; p < pEnd; ++p) {
4324     PetscInt dof, off, c;
4325 
4326     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4327     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4328     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
4329   }
4330   PetscCall(PetscSectionSetUp(mesh->supportSection));
4331   /* Calculate supports */
4332   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
4333   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
4334   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
4335   for (p = pStart; p < pEnd; ++p) {
4336     PetscInt dof, off, c;
4337 
4338     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4339     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4340     for (c = off; c < off + dof; ++c) {
4341       const PetscInt q = mesh->cones[c];
4342       PetscInt       offS;
4343 
4344       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
4345 
4346       mesh->supports[offS + offsets[q]] = p;
4347       ++offsets[q];
4348     }
4349   }
4350   PetscCall(PetscFree(offsets));
4351   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
4352   PetscFunctionReturn(PETSC_SUCCESS);
4353 }
4354 
4355 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
4356 {
4357   IS stratumIS;
4358 
4359   PetscFunctionBegin;
4360   if (pStart >= pEnd) PetscFunctionReturn(PETSC_SUCCESS);
4361   if (PetscDefined(USE_DEBUG)) {
4362     PetscInt  qStart, qEnd, numLevels, level;
4363     PetscBool overlap = PETSC_FALSE;
4364     PetscCall(DMLabelGetNumValues(label, &numLevels));
4365     for (level = 0; level < numLevels; level++) {
4366       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4367       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
4368         overlap = PETSC_TRUE;
4369         break;
4370       }
4371     }
4372     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);
4373   }
4374   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
4375   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
4376   PetscCall(ISDestroy(&stratumIS));
4377   PetscFunctionReturn(PETSC_SUCCESS);
4378 }
4379 
4380 static PetscErrorCode DMPlexStratify_CellType_Private(DM dm, DMLabel label)
4381 {
4382   PetscInt *pMin, *pMax;
4383   PetscInt  pStart, pEnd;
4384   PetscInt  dmin = PETSC_INT_MAX, dmax = PETSC_INT_MIN;
4385 
4386   PetscFunctionBegin;
4387   {
4388     DMLabel label2;
4389 
4390     PetscCall(DMPlexGetCellTypeLabel(dm, &label2));
4391     PetscCall(PetscObjectViewFromOptions((PetscObject)label2, NULL, "-ct_view"));
4392   }
4393   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4394   for (PetscInt p = pStart; p < pEnd; ++p) {
4395     DMPolytopeType ct;
4396 
4397     PetscCall(DMPlexGetCellType(dm, p, &ct));
4398     dmin = PetscMin(DMPolytopeTypeGetDim(ct), dmin);
4399     dmax = PetscMax(DMPolytopeTypeGetDim(ct), dmax);
4400   }
4401   PetscCall(PetscMalloc2(dmax + 1, &pMin, dmax + 1, &pMax));
4402   for (PetscInt d = dmin; d <= dmax; ++d) {
4403     pMin[d] = PETSC_INT_MAX;
4404     pMax[d] = PETSC_INT_MIN;
4405   }
4406   for (PetscInt p = pStart; p < pEnd; ++p) {
4407     DMPolytopeType ct;
4408     PetscInt       d;
4409 
4410     PetscCall(DMPlexGetCellType(dm, p, &ct));
4411     d       = DMPolytopeTypeGetDim(ct);
4412     pMin[d] = PetscMin(p, pMin[d]);
4413     pMax[d] = PetscMax(p, pMax[d]);
4414   }
4415   for (PetscInt d = dmin; d <= dmax; ++d) {
4416     if (pMin[d] > pMax[d]) continue;
4417     PetscCall(DMPlexCreateDepthStratum(dm, label, d, pMin[d], pMax[d] + 1));
4418   }
4419   PetscCall(PetscFree2(pMin, pMax));
4420   PetscFunctionReturn(PETSC_SUCCESS);
4421 }
4422 
4423 static PetscErrorCode DMPlexStratify_Topological_Private(DM dm, DMLabel label)
4424 {
4425   PetscInt pStart, pEnd;
4426   PetscInt numRoots = 0, numLeaves = 0;
4427 
4428   PetscFunctionBegin;
4429   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4430   {
4431     /* Initialize roots and count leaves */
4432     PetscInt sMin = PETSC_INT_MAX;
4433     PetscInt sMax = PETSC_INT_MIN;
4434     PetscInt coneSize, supportSize;
4435 
4436     for (PetscInt p = pStart; p < pEnd; ++p) {
4437       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4438       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4439       if (!coneSize && supportSize) {
4440         sMin = PetscMin(p, sMin);
4441         sMax = PetscMax(p, sMax);
4442         ++numRoots;
4443       } else if (!supportSize && coneSize) {
4444         ++numLeaves;
4445       } else if (!supportSize && !coneSize) {
4446         /* Isolated points */
4447         sMin = PetscMin(p, sMin);
4448         sMax = PetscMax(p, sMax);
4449       }
4450     }
4451     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4452   }
4453 
4454   if (numRoots + numLeaves == (pEnd - pStart)) {
4455     PetscInt sMin = PETSC_INT_MAX;
4456     PetscInt sMax = PETSC_INT_MIN;
4457     PetscInt coneSize, supportSize;
4458 
4459     for (PetscInt p = pStart; p < pEnd; ++p) {
4460       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4461       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4462       if (!supportSize && coneSize) {
4463         sMin = PetscMin(p, sMin);
4464         sMax = PetscMax(p, sMax);
4465       }
4466     }
4467     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4468   } else {
4469     PetscInt level = 0;
4470     PetscInt qStart, qEnd;
4471 
4472     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4473     while (qEnd > qStart) {
4474       PetscInt sMin = PETSC_INT_MAX;
4475       PetscInt sMax = PETSC_INT_MIN;
4476 
4477       for (PetscInt q = qStart; q < qEnd; ++q) {
4478         const PetscInt *support;
4479         PetscInt        supportSize;
4480 
4481         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4482         PetscCall(DMPlexGetSupport(dm, q, &support));
4483         for (PetscInt s = 0; s < supportSize; ++s) {
4484           sMin = PetscMin(support[s], sMin);
4485           sMax = PetscMax(support[s], sMax);
4486         }
4487       }
4488       PetscCall(DMLabelGetNumValues(label, &level));
4489       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4490       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4491     }
4492   }
4493   PetscFunctionReturn(PETSC_SUCCESS);
4494 }
4495 
4496 /*@
4497   DMPlexStratify - Computes the strata for all points in the `DMPLEX`
4498 
4499   Collective
4500 
4501   Input Parameter:
4502 . dm - The `DMPLEX`
4503 
4504   Level: beginner
4505 
4506   Notes:
4507   The strata group all points of the same grade, and this function calculates the strata. This
4508   grade can be seen as the height (or depth) of the point in the DAG.
4509 
4510   The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
4511   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram).
4512   Concretely, `DMPlexStratify()` creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
4513   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
4514   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through `DMPlexGetDepthLabel()` or `DMPlexGetDepthStratum()`, or
4515   manually via `DMGetLabel()`.  The height is defined implicitly by height = maxDimension - depth, and can be accessed
4516   via `DMPlexGetHeightStratum()`.  For example, cells have height 0 and faces have height 1.
4517 
4518   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
4519   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
4520   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
4521   to interpolate only that one (e0), so that
4522 .vb
4523   cone(c0) = {e0, v2}
4524   cone(e0) = {v0, v1}
4525 .ve
4526   If `DMPlexStratify()` is run on this mesh, it will give depths
4527 .vb
4528    depth 0 = {v0, v1, v2}
4529    depth 1 = {e0, c0}
4530 .ve
4531   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
4532 
4533   `DMPlexStratify()` should be called after all calls to `DMPlexSymmetrize()`
4534 
4535 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
4536 @*/
4537 PetscErrorCode DMPlexStratify(DM dm)
4538 {
4539   DM_Plex  *mesh = (DM_Plex *)dm->data;
4540   DMLabel   label;
4541   PetscBool flg = PETSC_FALSE;
4542 
4543   PetscFunctionBegin;
4544   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4545   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
4546 
4547   // Create depth label
4548   PetscCall(DMRemoveLabel(dm, "depth", NULL));
4549   PetscCall(DMCreateLabel(dm, "depth"));
4550   PetscCall(DMPlexGetDepthLabel(dm, &label));
4551 
4552   PetscCall(PetscOptionsGetBool(NULL, dm->hdr.prefix, "-dm_plex_stratify_celltype", &flg, NULL));
4553   if (flg) PetscCall(DMPlexStratify_CellType_Private(dm, label));
4554   else PetscCall(DMPlexStratify_Topological_Private(dm, label));
4555 
4556   { /* just in case there is an empty process */
4557     PetscInt numValues, maxValues = 0, v;
4558 
4559     PetscCall(DMLabelGetNumValues(label, &numValues));
4560     PetscCallMPI(MPIU_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4561     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4562   }
4563   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4564   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4565   PetscFunctionReturn(PETSC_SUCCESS);
4566 }
4567 
4568 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4569 {
4570   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4571   PetscInt       dim, depth, pheight, coneSize;
4572 
4573   PetscFunctionBeginHot;
4574   PetscCall(DMGetDimension(dm, &dim));
4575   PetscCall(DMPlexGetDepth(dm, &depth));
4576   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4577   pheight = depth - pdepth;
4578   if (depth <= 1) {
4579     switch (pdepth) {
4580     case 0:
4581       ct = DM_POLYTOPE_POINT;
4582       break;
4583     case 1:
4584       switch (coneSize) {
4585       case 2:
4586         ct = DM_POLYTOPE_SEGMENT;
4587         break;
4588       case 3:
4589         ct = DM_POLYTOPE_TRIANGLE;
4590         break;
4591       case 4:
4592         switch (dim) {
4593         case 2:
4594           ct = DM_POLYTOPE_QUADRILATERAL;
4595           break;
4596         case 3:
4597           ct = DM_POLYTOPE_TETRAHEDRON;
4598           break;
4599         default:
4600           break;
4601         }
4602         break;
4603       case 5:
4604         ct = DM_POLYTOPE_PYRAMID;
4605         break;
4606       case 6:
4607         ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4608         break;
4609       case 8:
4610         ct = DM_POLYTOPE_HEXAHEDRON;
4611         break;
4612       default:
4613         break;
4614       }
4615     }
4616   } else {
4617     if (pdepth == 0) {
4618       ct = DM_POLYTOPE_POINT;
4619     } else if (pheight == 0) {
4620       switch (dim) {
4621       case 1:
4622         switch (coneSize) {
4623         case 2:
4624           ct = DM_POLYTOPE_SEGMENT;
4625           break;
4626         default:
4627           break;
4628         }
4629         break;
4630       case 2:
4631         switch (coneSize) {
4632         case 3:
4633           ct = DM_POLYTOPE_TRIANGLE;
4634           break;
4635         case 4:
4636           ct = DM_POLYTOPE_QUADRILATERAL;
4637           break;
4638         default:
4639           break;
4640         }
4641         break;
4642       case 3:
4643         switch (coneSize) {
4644         case 4:
4645           ct = DM_POLYTOPE_TETRAHEDRON;
4646           break;
4647         case 5: {
4648           const PetscInt *cone;
4649           PetscInt        faceConeSize;
4650 
4651           PetscCall(DMPlexGetCone(dm, p, &cone));
4652           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4653           switch (faceConeSize) {
4654           case 3:
4655             ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4656             break;
4657           case 4:
4658             ct = DM_POLYTOPE_PYRAMID;
4659             break;
4660           }
4661         } break;
4662         case 6:
4663           ct = DM_POLYTOPE_HEXAHEDRON;
4664           break;
4665         default:
4666           break;
4667         }
4668         break;
4669       default:
4670         break;
4671       }
4672     } else if (pheight > 0) {
4673       switch (coneSize) {
4674       case 2:
4675         ct = DM_POLYTOPE_SEGMENT;
4676         break;
4677       case 3:
4678         ct = DM_POLYTOPE_TRIANGLE;
4679         break;
4680       case 4:
4681         ct = DM_POLYTOPE_QUADRILATERAL;
4682         break;
4683       default:
4684         break;
4685       }
4686     }
4687   }
4688   *pt = ct;
4689   PetscFunctionReturn(PETSC_SUCCESS);
4690 }
4691 
4692 /*@
4693   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4694 
4695   Collective
4696 
4697   Input Parameter:
4698 . dm - The `DMPLEX`
4699 
4700   Level: developer
4701 
4702   Note:
4703   This function is normally called automatically when a cell type is requested. It creates an
4704   internal `DMLabel` named "celltype" which can be directly accessed using `DMGetLabel()`. A user may disable
4705   automatic creation by creating the label manually, using `DMCreateLabel`(dm, "celltype").
4706 
4707   `DMPlexComputeCellTypes()` should be called after all calls to `DMPlexSymmetrize()` and `DMPlexStratify()`
4708 
4709 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4710 @*/
4711 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4712 {
4713   DM_Plex *mesh;
4714   DMLabel  ctLabel;
4715   PetscInt pStart, pEnd, p;
4716 
4717   PetscFunctionBegin;
4718   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4719   mesh = (DM_Plex *)dm->data;
4720   PetscCall(DMCreateLabel(dm, "celltype"));
4721   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4722   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4723   PetscCall(PetscFree(mesh->cellTypes));
4724   PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
4725   for (p = pStart; p < pEnd; ++p) {
4726     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4727     PetscInt       pdepth;
4728 
4729     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4730     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4731     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]);
4732     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4733     mesh->cellTypes[p - pStart].value_as_uint8 = (uint8_t)ct;
4734   }
4735   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4736   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4737   PetscFunctionReturn(PETSC_SUCCESS);
4738 }
4739 
4740 /*@C
4741   DMPlexGetJoin - Get an array for the join of the set of points
4742 
4743   Not Collective
4744 
4745   Input Parameters:
4746 + dm        - The `DMPLEX` object
4747 . numPoints - The number of input points for the join
4748 - points    - The input points
4749 
4750   Output Parameters:
4751 + numCoveredPoints - The number of points in the join
4752 - coveredPoints    - The points in the join
4753 
4754   Level: intermediate
4755 
4756   Note:
4757   Currently, this is restricted to a single level join
4758 
4759   Fortran Notes:
4760   `converedPoints` must be declared with
4761 .vb
4762   PetscInt, pointer :: coveredPints(:)
4763 .ve
4764 
4765   The `numCoveredPoints` argument is not present in the Fortran binding.
4766 
4767 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4768 @*/
4769 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4770 {
4771   DM_Plex  *mesh = (DM_Plex *)dm->data;
4772   PetscInt *join[2];
4773   PetscInt  joinSize, i = 0;
4774   PetscInt  dof, off, p, c, m;
4775   PetscInt  maxSupportSize;
4776 
4777   PetscFunctionBegin;
4778   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4779   PetscAssertPointer(points, 3);
4780   PetscAssertPointer(numCoveredPoints, 4);
4781   PetscAssertPointer(coveredPoints, 5);
4782   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4783   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4784   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4785   /* Copy in support of first point */
4786   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4787   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4788   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4789   /* Check each successive support */
4790   for (p = 1; p < numPoints; ++p) {
4791     PetscInt newJoinSize = 0;
4792 
4793     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4794     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4795     for (c = 0; c < dof; ++c) {
4796       const PetscInt point = mesh->supports[off + c];
4797 
4798       for (m = 0; m < joinSize; ++m) {
4799         if (point == join[i][m]) {
4800           join[1 - i][newJoinSize++] = point;
4801           break;
4802         }
4803       }
4804     }
4805     joinSize = newJoinSize;
4806     i        = 1 - i;
4807   }
4808   *numCoveredPoints = joinSize;
4809   *coveredPoints    = join[i];
4810   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4811   PetscFunctionReturn(PETSC_SUCCESS);
4812 }
4813 
4814 /*@C
4815   DMPlexRestoreJoin - Restore an array for the join of the set of points obtained with `DMPlexGetJoin()`
4816 
4817   Not Collective
4818 
4819   Input Parameters:
4820 + dm        - The `DMPLEX` object
4821 . numPoints - The number of input points for the join
4822 - points    - The input points
4823 
4824   Output Parameters:
4825 + numCoveredPoints - The number of points in the join
4826 - coveredPoints    - The points in the join
4827 
4828   Level: intermediate
4829 
4830   Fortran Notes:
4831   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4832 
4833 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4834 @*/
4835 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4836 {
4837   PetscFunctionBegin;
4838   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4839   if (points) PetscAssertPointer(points, 3);
4840   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4841   PetscAssertPointer(coveredPoints, 5);
4842   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4843   if (numCoveredPoints) *numCoveredPoints = 0;
4844   PetscFunctionReturn(PETSC_SUCCESS);
4845 }
4846 
4847 /*@C
4848   DMPlexGetFullJoin - Get an array for the join of the set of points
4849 
4850   Not Collective
4851 
4852   Input Parameters:
4853 + dm        - The `DMPLEX` object
4854 . numPoints - The number of input points for the join
4855 - points    - The input points, its length is `numPoints`
4856 
4857   Output Parameters:
4858 + numCoveredPoints - The number of points in the join
4859 - coveredPoints    - The points in the join, its length is `numCoveredPoints`
4860 
4861   Level: intermediate
4862 
4863   Fortran Notes:
4864   `points` and `converedPoints` must be declared with
4865 .vb
4866   PetscInt, pointer :: points(:)
4867   PetscInt, pointer :: coveredPints(:)
4868 .ve
4869 
4870   The `numCoveredPoints` argument is not present in the Fortran binding.
4871 
4872 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4873 @*/
4874 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4875 {
4876   PetscInt *offsets, **closures;
4877   PetscInt *join[2];
4878   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4879   PetscInt  p, d, c, m, ms;
4880 
4881   PetscFunctionBegin;
4882   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4883   PetscAssertPointer(points, 3);
4884   PetscAssertPointer(numCoveredPoints, 4);
4885   PetscAssertPointer(coveredPoints, 5);
4886 
4887   PetscCall(DMPlexGetDepth(dm, &depth));
4888   PetscCall(PetscCalloc1(numPoints, &closures));
4889   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4890   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4891   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4892   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4893   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4894 
4895   for (p = 0; p < numPoints; ++p) {
4896     PetscInt closureSize;
4897 
4898     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4899 
4900     offsets[p * (depth + 2) + 0] = 0;
4901     for (d = 0; d < depth + 1; ++d) {
4902       PetscInt pStart, pEnd, i;
4903 
4904       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4905       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4906         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4907           offsets[p * (depth + 2) + d + 1] = i;
4908           break;
4909         }
4910       }
4911       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4912     }
4913     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);
4914   }
4915   for (d = 0; d < depth + 1; ++d) {
4916     PetscInt dof;
4917 
4918     /* Copy in support of first point */
4919     dof = offsets[d + 1] - offsets[d];
4920     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4921     /* Check each successive cone */
4922     for (p = 1; p < numPoints && joinSize; ++p) {
4923       PetscInt newJoinSize = 0;
4924 
4925       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
4926       for (c = 0; c < dof; ++c) {
4927         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
4928 
4929         for (m = 0; m < joinSize; ++m) {
4930           if (point == join[i][m]) {
4931             join[1 - i][newJoinSize++] = point;
4932             break;
4933           }
4934         }
4935       }
4936       joinSize = newJoinSize;
4937       i        = 1 - i;
4938     }
4939     if (joinSize) break;
4940   }
4941   *numCoveredPoints = joinSize;
4942   *coveredPoints    = join[i];
4943   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4944   PetscCall(PetscFree(closures));
4945   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4946   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
4947   PetscFunctionReturn(PETSC_SUCCESS);
4948 }
4949 
4950 /*@C
4951   DMPlexGetMeet - Get an array for the meet of the set of points
4952 
4953   Not Collective
4954 
4955   Input Parameters:
4956 + dm        - The `DMPLEX` object
4957 . numPoints - The number of input points for the meet
4958 - points    - The input points, of length `numPoints`
4959 
4960   Output Parameters:
4961 + numCoveringPoints - The number of points in the meet
4962 - coveringPoints    - The points in the meet, of length `numCoveringPoints`
4963 
4964   Level: intermediate
4965 
4966   Note:
4967   Currently, this is restricted to a single level meet
4968 
4969   Fortran Notes:
4970   `coveringPoints` must be declared with
4971 .vb
4972   PetscInt, pointer :: coveringPoints(:)
4973 .ve
4974 
4975   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4976 
4977 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4978 @*/
4979 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt *coveringPoints[])
4980 {
4981   DM_Plex  *mesh = (DM_Plex *)dm->data;
4982   PetscInt *meet[2];
4983   PetscInt  meetSize, i = 0;
4984   PetscInt  dof, off, p, c, m;
4985   PetscInt  maxConeSize;
4986 
4987   PetscFunctionBegin;
4988   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4989   PetscAssertPointer(points, 3);
4990   PetscAssertPointer(numCoveringPoints, 4);
4991   PetscAssertPointer(coveringPoints, 5);
4992   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4993   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4994   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4995   /* Copy in cone of first point */
4996   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4997   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4998   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
4999   /* Check each successive cone */
5000   for (p = 1; p < numPoints; ++p) {
5001     PetscInt newMeetSize = 0;
5002 
5003     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
5004     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
5005     for (c = 0; c < dof; ++c) {
5006       const PetscInt point = mesh->cones[off + c];
5007 
5008       for (m = 0; m < meetSize; ++m) {
5009         if (point == meet[i][m]) {
5010           meet[1 - i][newMeetSize++] = point;
5011           break;
5012         }
5013       }
5014     }
5015     meetSize = newMeetSize;
5016     i        = 1 - i;
5017   }
5018   *numCoveringPoints = meetSize;
5019   *coveringPoints    = meet[i];
5020   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
5021   PetscFunctionReturn(PETSC_SUCCESS);
5022 }
5023 
5024 /*@C
5025   DMPlexRestoreMeet - Restore an array for the meet of the set of points obtained with `DMPlexGetMeet()`
5026 
5027   Not Collective
5028 
5029   Input Parameters:
5030 + dm        - The `DMPLEX` object
5031 . numPoints - The number of input points for the meet
5032 - points    - The input points
5033 
5034   Output Parameters:
5035 + numCoveredPoints - The number of points in the meet
5036 - coveredPoints    - The points in the meet
5037 
5038   Level: intermediate
5039 
5040   Fortran Notes:
5041   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
5042 
5043 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
5044 @*/
5045 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
5046 {
5047   PetscFunctionBegin;
5048   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5049   if (points) PetscAssertPointer(points, 3);
5050   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
5051   PetscAssertPointer(coveredPoints, 5);
5052   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
5053   if (numCoveredPoints) *numCoveredPoints = 0;
5054   PetscFunctionReturn(PETSC_SUCCESS);
5055 }
5056 
5057 /*@C
5058   DMPlexGetFullMeet - Get an array for the meet of the set of points
5059 
5060   Not Collective
5061 
5062   Input Parameters:
5063 + dm        - The `DMPLEX` object
5064 . numPoints - The number of input points for the meet
5065 - points    - The input points, of length  `numPoints`
5066 
5067   Output Parameters:
5068 + numCoveredPoints - The number of points in the meet
5069 - coveredPoints    - The points in the meet, of length  `numCoveredPoints`
5070 
5071   Level: intermediate
5072 
5073   Fortran Notes:
5074   `points` and `coveredPoints` must be declared with
5075 .vb
5076   PetscInt, pointer :: points(:)
5077   PetscInt, pointer :: coveredPoints(:)
5078 .ve
5079 
5080   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
5081 
5082 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
5083 @*/
5084 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
5085 {
5086   PetscInt *offsets, **closures;
5087   PetscInt *meet[2];
5088   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
5089   PetscInt  p, h, c, m, mc;
5090 
5091   PetscFunctionBegin;
5092   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5093   PetscAssertPointer(points, 3);
5094   PetscAssertPointer(numCoveredPoints, 4);
5095   PetscAssertPointer(coveredPoints, 5);
5096 
5097   PetscCall(DMPlexGetDepth(dm, &height));
5098   PetscCall(PetscMalloc1(numPoints, &closures));
5099   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5100   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
5101   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
5102   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
5103   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
5104 
5105   for (p = 0; p < numPoints; ++p) {
5106     PetscInt closureSize;
5107 
5108     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
5109 
5110     offsets[p * (height + 2) + 0] = 0;
5111     for (h = 0; h < height + 1; ++h) {
5112       PetscInt pStart, pEnd, i;
5113 
5114       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
5115       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
5116         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
5117           offsets[p * (height + 2) + h + 1] = i;
5118           break;
5119         }
5120       }
5121       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
5122     }
5123     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);
5124   }
5125   for (h = 0; h < height + 1; ++h) {
5126     PetscInt dof;
5127 
5128     /* Copy in cone of first point */
5129     dof = offsets[h + 1] - offsets[h];
5130     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
5131     /* Check each successive cone */
5132     for (p = 1; p < numPoints && meetSize; ++p) {
5133       PetscInt newMeetSize = 0;
5134 
5135       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
5136       for (c = 0; c < dof; ++c) {
5137         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
5138 
5139         for (m = 0; m < meetSize; ++m) {
5140           if (point == meet[i][m]) {
5141             meet[1 - i][newMeetSize++] = point;
5142             break;
5143           }
5144         }
5145       }
5146       meetSize = newMeetSize;
5147       i        = 1 - i;
5148     }
5149     if (meetSize) break;
5150   }
5151   *numCoveredPoints = meetSize;
5152   *coveredPoints    = meet[i];
5153   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
5154   PetscCall(PetscFree(closures));
5155   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5156   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
5157   PetscFunctionReturn(PETSC_SUCCESS);
5158 }
5159 
5160 /*@
5161   DMPlexEqual - Determine if two `DM` have the same topology
5162 
5163   Not Collective
5164 
5165   Input Parameters:
5166 + dmA - A `DMPLEX` object
5167 - dmB - A `DMPLEX` object
5168 
5169   Output Parameter:
5170 . equal - `PETSC_TRUE` if the topologies are identical
5171 
5172   Level: intermediate
5173 
5174   Note:
5175   We are not solving graph isomorphism, so we do not permute.
5176 
5177 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5178 @*/
5179 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
5180 {
5181   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
5182 
5183   PetscFunctionBegin;
5184   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
5185   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
5186   PetscAssertPointer(equal, 3);
5187 
5188   *equal = PETSC_FALSE;
5189   PetscCall(DMPlexGetDepth(dmA, &depth));
5190   PetscCall(DMPlexGetDepth(dmB, &depthB));
5191   if (depth != depthB) PetscFunctionReturn(PETSC_SUCCESS);
5192   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
5193   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
5194   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(PETSC_SUCCESS);
5195   for (p = pStart; p < pEnd; ++p) {
5196     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
5197     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
5198 
5199     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
5200     PetscCall(DMPlexGetCone(dmA, p, &cone));
5201     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
5202     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
5203     PetscCall(DMPlexGetCone(dmB, p, &coneB));
5204     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
5205     if (coneSize != coneSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5206     for (c = 0; c < coneSize; ++c) {
5207       if (cone[c] != coneB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5208       if (ornt[c] != orntB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5209     }
5210     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
5211     PetscCall(DMPlexGetSupport(dmA, p, &support));
5212     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
5213     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
5214     if (supportSize != supportSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5215     for (s = 0; s < supportSize; ++s) {
5216       if (support[s] != supportB[s]) PetscFunctionReturn(PETSC_SUCCESS);
5217     }
5218   }
5219   *equal = PETSC_TRUE;
5220   PetscFunctionReturn(PETSC_SUCCESS);
5221 }
5222 
5223 /*@
5224   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
5225 
5226   Not Collective
5227 
5228   Input Parameters:
5229 + dm         - The `DMPLEX`
5230 . cellDim    - The cell dimension
5231 - numCorners - The number of vertices on a cell
5232 
5233   Output Parameter:
5234 . numFaceVertices - The number of vertices on a face
5235 
5236   Level: developer
5237 
5238   Note:
5239   Of course this can only work for a restricted set of symmetric shapes
5240 
5241 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5242 @*/
5243 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
5244 {
5245   MPI_Comm comm;
5246 
5247   PetscFunctionBegin;
5248   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
5249   PetscAssertPointer(numFaceVertices, 4);
5250   switch (cellDim) {
5251   case 0:
5252     *numFaceVertices = 0;
5253     break;
5254   case 1:
5255     *numFaceVertices = 1;
5256     break;
5257   case 2:
5258     switch (numCorners) {
5259     case 3:                 /* triangle */
5260       *numFaceVertices = 2; /* Edge has 2 vertices */
5261       break;
5262     case 4:                 /* quadrilateral */
5263       *numFaceVertices = 2; /* Edge has 2 vertices */
5264       break;
5265     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
5266       *numFaceVertices = 3; /* Edge has 3 vertices */
5267       break;
5268     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
5269       *numFaceVertices = 3; /* Edge has 3 vertices */
5270       break;
5271     default:
5272       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5273     }
5274     break;
5275   case 3:
5276     switch (numCorners) {
5277     case 4:                 /* tetradehdron */
5278       *numFaceVertices = 3; /* Face has 3 vertices */
5279       break;
5280     case 6:                 /* tet cohesive cells */
5281       *numFaceVertices = 4; /* Face has 4 vertices */
5282       break;
5283     case 8:                 /* hexahedron */
5284       *numFaceVertices = 4; /* Face has 4 vertices */
5285       break;
5286     case 9:                 /* tet cohesive Lagrange cells */
5287       *numFaceVertices = 6; /* Face has 6 vertices */
5288       break;
5289     case 10:                /* quadratic tetrahedron */
5290       *numFaceVertices = 6; /* Face has 6 vertices */
5291       break;
5292     case 12:                /* hex cohesive Lagrange cells */
5293       *numFaceVertices = 6; /* Face has 6 vertices */
5294       break;
5295     case 18:                /* quadratic tet cohesive Lagrange cells */
5296       *numFaceVertices = 6; /* Face has 6 vertices */
5297       break;
5298     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
5299       *numFaceVertices = 9; /* Face has 9 vertices */
5300       break;
5301     default:
5302       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5303     }
5304     break;
5305   default:
5306     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
5307   }
5308   PetscFunctionReturn(PETSC_SUCCESS);
5309 }
5310 
5311 /*@
5312   DMPlexGetDepthLabel - Get the `DMLabel` recording the depth of each point
5313 
5314   Not Collective
5315 
5316   Input Parameter:
5317 . dm - The `DMPLEX` object
5318 
5319   Output Parameter:
5320 . depthLabel - The `DMLabel` recording point depth
5321 
5322   Level: developer
5323 
5324 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
5325 @*/
5326 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
5327 {
5328   PetscFunctionBegin;
5329   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5330   PetscAssertPointer(depthLabel, 2);
5331   *depthLabel = dm->depthLabel;
5332   PetscFunctionReturn(PETSC_SUCCESS);
5333 }
5334 
5335 /*@
5336   DMPlexGetDepth - Get the depth of the DAG representing this mesh
5337 
5338   Not Collective
5339 
5340   Input Parameter:
5341 . dm - The `DMPLEX` object
5342 
5343   Output Parameter:
5344 . depth - The number of strata (breadth first levels) in the DAG
5345 
5346   Level: developer
5347 
5348   Notes:
5349   This returns maximum of point depths over all points, i.e. maximum value of the label returned by `DMPlexGetDepthLabel()`.
5350 
5351   The point depth is described more in detail in `DMPlexGetDepthStratum()`.
5352 
5353   An empty mesh gives -1.
5354 
5355 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
5356 @*/
5357 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
5358 {
5359   DM_Plex *mesh = (DM_Plex *)dm->data;
5360   DMLabel  label;
5361   PetscInt d = -1;
5362 
5363   PetscFunctionBegin;
5364   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5365   PetscAssertPointer(depth, 2);
5366   if (mesh->tr) {
5367     PetscCall(DMPlexTransformGetDepth(mesh->tr, depth));
5368   } else {
5369     PetscCall(DMPlexGetDepthLabel(dm, &label));
5370     // Allow missing depths
5371     if (label) PetscCall(DMLabelGetValueBounds(label, NULL, &d));
5372     *depth = d;
5373   }
5374   PetscFunctionReturn(PETSC_SUCCESS);
5375 }
5376 
5377 /*@
5378   DMPlexGetDepthStratum - Get the bounds [`start`, `end`) for all points at a certain depth.
5379 
5380   Not Collective
5381 
5382   Input Parameters:
5383 + dm    - The `DMPLEX` object
5384 - depth - The requested depth
5385 
5386   Output Parameters:
5387 + start - The first point at this `depth`
5388 - end   - One beyond the last point at this `depth`
5389 
5390   Level: developer
5391 
5392   Notes:
5393   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
5394   often "vertices".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then depth stratum 1 contains the next
5395   higher dimension, e.g., "edges".
5396 
5397 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetHeightStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
5398 @*/
5399 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
5400 {
5401   DM_Plex *mesh = (DM_Plex *)dm->data;
5402   DMLabel  label;
5403   PetscInt pStart, pEnd;
5404 
5405   PetscFunctionBegin;
5406   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5407   if (start) {
5408     PetscAssertPointer(start, 3);
5409     *start = 0;
5410   }
5411   if (end) {
5412     PetscAssertPointer(end, 4);
5413     *end = 0;
5414   }
5415   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5416   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5417   if (depth < 0) {
5418     if (start) *start = pStart;
5419     if (end) *end = pEnd;
5420     PetscFunctionReturn(PETSC_SUCCESS);
5421   }
5422   if (mesh->tr) {
5423     PetscCall(DMPlexTransformGetDepthStratum(mesh->tr, depth, start, end));
5424   } else {
5425     PetscCall(DMPlexGetDepthLabel(dm, &label));
5426     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5427     PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
5428   }
5429   PetscFunctionReturn(PETSC_SUCCESS);
5430 }
5431 
5432 /*@
5433   DMPlexGetHeightStratum - Get the bounds [`start`, `end`) for all points at a certain height.
5434 
5435   Not Collective
5436 
5437   Input Parameters:
5438 + dm     - The `DMPLEX` object
5439 - height - The requested height
5440 
5441   Output Parameters:
5442 + start - The first point at this `height`
5443 - end   - One beyond the last point at this `height`
5444 
5445   Level: developer
5446 
5447   Notes:
5448   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
5449   points, often called "cells" or "elements".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then height
5450   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
5451 
5452 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5453 @*/
5454 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
5455 {
5456   DMLabel  label;
5457   PetscInt depth, pStart, pEnd;
5458 
5459   PetscFunctionBegin;
5460   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5461   if (start) {
5462     PetscAssertPointer(start, 3);
5463     *start = 0;
5464   }
5465   if (end) {
5466     PetscAssertPointer(end, 4);
5467     *end = 0;
5468   }
5469   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5470   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5471   if (height < 0) {
5472     if (start) *start = pStart;
5473     if (end) *end = pEnd;
5474     PetscFunctionReturn(PETSC_SUCCESS);
5475   }
5476   PetscCall(DMPlexGetDepthLabel(dm, &label));
5477   if (label) PetscCall(DMLabelGetNumValues(label, &depth));
5478   else PetscCall(DMGetDimension(dm, &depth));
5479   PetscCheck(depth >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Depth not yet computed");
5480   PetscCall(DMPlexGetDepthStratum(dm, depth - 1 - height, start, end));
5481   PetscFunctionReturn(PETSC_SUCCESS);
5482 }
5483 
5484 /*@
5485   DMPlexGetPointDepth - Get the `depth` of a given point
5486 
5487   Not Collective
5488 
5489   Input Parameters:
5490 + dm    - The `DMPLEX` object
5491 - point - The point
5492 
5493   Output Parameter:
5494 . depth - The depth of the `point`
5495 
5496   Level: intermediate
5497 
5498 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5499 @*/
5500 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
5501 {
5502   PetscFunctionBegin;
5503   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5504   PetscAssertPointer(depth, 3);
5505   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
5506   PetscFunctionReturn(PETSC_SUCCESS);
5507 }
5508 
5509 /*@
5510   DMPlexGetPointHeight - Get the `height` of a given point
5511 
5512   Not Collective
5513 
5514   Input Parameters:
5515 + dm    - The `DMPLEX` object
5516 - point - The point
5517 
5518   Output Parameter:
5519 . height - The height of the `point`
5520 
5521   Level: intermediate
5522 
5523 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
5524 @*/
5525 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5526 {
5527   PetscInt n, pDepth;
5528 
5529   PetscFunctionBegin;
5530   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5531   PetscAssertPointer(height, 3);
5532   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5533   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5534   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5535   PetscFunctionReturn(PETSC_SUCCESS);
5536 }
5537 
5538 /*@
5539   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5540 
5541   Not Collective
5542 
5543   Input Parameter:
5544 . dm - The `DMPLEX` object
5545 
5546   Output Parameter:
5547 . celltypeLabel - The `DMLabel` recording cell polytope type
5548 
5549   Level: developer
5550 
5551   Note:
5552   This function will trigger automatica computation of cell types. This can be disabled by calling
5553   `DMCreateLabel`(dm, "celltype") beforehand.
5554 
5555 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5556 @*/
5557 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5558 {
5559   PetscFunctionBegin;
5560   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5561   PetscAssertPointer(celltypeLabel, 2);
5562   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5563   *celltypeLabel = dm->celltypeLabel;
5564   PetscFunctionReturn(PETSC_SUCCESS);
5565 }
5566 
5567 /*@
5568   DMPlexGetCellType - Get the polytope type of a given cell
5569 
5570   Not Collective
5571 
5572   Input Parameters:
5573 + dm   - The `DMPLEX` object
5574 - cell - The cell
5575 
5576   Output Parameter:
5577 . celltype - The polytope type of the cell
5578 
5579   Level: intermediate
5580 
5581 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeType`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5582 @*/
5583 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5584 {
5585   DM_Plex *mesh = (DM_Plex *)dm->data;
5586   DMLabel  label;
5587   PetscInt ct;
5588 
5589   PetscFunctionBegin;
5590   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5591   PetscAssertPointer(celltype, 3);
5592   if (mesh->tr) {
5593     PetscCall(DMPlexTransformGetCellType(mesh->tr, cell, celltype));
5594   } else {
5595     PetscInt pStart, pEnd;
5596 
5597     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, NULL));
5598     if (!mesh->cellTypes) { /* XXX remove? optimize? */
5599       PetscCall(PetscSectionGetChart(mesh->coneSection, NULL, &pEnd));
5600       PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5601       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5602       for (PetscInt p = pStart; p < pEnd; p++) {
5603         PetscCall(DMLabelGetValue(label, p, &ct));
5604         mesh->cellTypes[p - pStart].value_as_uint8 = (uint8_t)ct;
5605       }
5606     }
5607     *celltype = (DMPolytopeType)mesh->cellTypes[cell - pStart].value_as_uint8;
5608     if (PetscDefined(USE_DEBUG)) {
5609       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5610       PetscCall(DMLabelGetValue(label, cell, &ct));
5611       PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5612       PetscCheck(ct == (PetscInt)*celltype, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid cellType for %" PetscInt_FMT ": %d != %" PetscInt_FMT, cell, (int)*celltype, ct);
5613     }
5614   }
5615   PetscFunctionReturn(PETSC_SUCCESS);
5616 }
5617 
5618 /*@
5619   DMPlexSetCellType - Set the polytope type of a given cell
5620 
5621   Not Collective
5622 
5623   Input Parameters:
5624 + dm       - The `DMPLEX` object
5625 . cell     - The cell
5626 - celltype - The polytope type of the cell
5627 
5628   Level: advanced
5629 
5630   Note:
5631   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5632   is executed. This function will override the computed type. However, if automatic classification will not succeed
5633   and a user wants to manually specify all types, the classification must be disabled by calling
5634   DMCreateLabel(dm, "celltype") before getting or setting any cell types.
5635 
5636 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5637 @*/
5638 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5639 {
5640   DM_Plex *mesh = (DM_Plex *)dm->data;
5641   DMLabel  label;
5642   PetscInt pStart, pEnd;
5643 
5644   PetscFunctionBegin;
5645   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5646   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
5647   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5648   PetscCall(DMLabelSetValue(label, cell, celltype));
5649   if (!mesh->cellTypes) PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5650   mesh->cellTypes[cell - pStart].value_as_uint8 = (uint8_t)celltype;
5651   PetscFunctionReturn(PETSC_SUCCESS);
5652 }
5653 
5654 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5655 {
5656   PetscSection section;
5657   PetscInt     maxHeight;
5658   const char  *prefix;
5659 
5660   PetscFunctionBegin;
5661   PetscCall(DMClone(dm, cdm));
5662   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5663   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5664   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5665   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5666   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5667   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5668   PetscCall(DMSetLocalSection(*cdm, section));
5669   PetscCall(PetscSectionDestroy(&section));
5670 
5671   PetscCall(DMSetNumFields(*cdm, 1));
5672   PetscCall(DMCreateDS(*cdm));
5673   (*cdm)->cloneOpts = PETSC_TRUE;
5674   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5675   PetscFunctionReturn(PETSC_SUCCESS);
5676 }
5677 
5678 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5679 {
5680   Vec coordsLocal, cellCoordsLocal;
5681   DM  coordsDM, cellCoordsDM;
5682 
5683   PetscFunctionBegin;
5684   *field = NULL;
5685   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5686   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5687   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5688   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5689   if (coordsLocal && coordsDM) {
5690     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5691     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5692   }
5693   PetscFunctionReturn(PETSC_SUCCESS);
5694 }
5695 
5696 /*@
5697   DMPlexGetConeSection - Return a section which describes the layout of cone data
5698 
5699   Not Collective
5700 
5701   Input Parameter:
5702 . dm - The `DMPLEX` object
5703 
5704   Output Parameter:
5705 . section - The `PetscSection` object
5706 
5707   Level: developer
5708 
5709 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5710 @*/
5711 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5712 {
5713   DM_Plex *mesh = (DM_Plex *)dm->data;
5714 
5715   PetscFunctionBegin;
5716   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5717   if (section) *section = mesh->coneSection;
5718   PetscFunctionReturn(PETSC_SUCCESS);
5719 }
5720 
5721 /*@
5722   DMPlexGetSupportSection - Return a section which describes the layout of support data
5723 
5724   Not Collective
5725 
5726   Input Parameter:
5727 . dm - The `DMPLEX` object
5728 
5729   Output Parameter:
5730 . section - The `PetscSection` object
5731 
5732   Level: developer
5733 
5734 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5735 @*/
5736 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5737 {
5738   DM_Plex *mesh = (DM_Plex *)dm->data;
5739 
5740   PetscFunctionBegin;
5741   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5742   if (section) *section = mesh->supportSection;
5743   PetscFunctionReturn(PETSC_SUCCESS);
5744 }
5745 
5746 /*@C
5747   DMPlexGetCones - Return cone data
5748 
5749   Not Collective
5750 
5751   Input Parameter:
5752 . dm - The `DMPLEX` object
5753 
5754   Output Parameter:
5755 . cones - The cone for each point
5756 
5757   Level: developer
5758 
5759 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5760 @*/
5761 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5762 {
5763   DM_Plex *mesh = (DM_Plex *)dm->data;
5764 
5765   PetscFunctionBegin;
5766   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5767   if (cones) *cones = mesh->cones;
5768   PetscFunctionReturn(PETSC_SUCCESS);
5769 }
5770 
5771 /*@C
5772   DMPlexGetConeOrientations - Return cone orientation data
5773 
5774   Not Collective
5775 
5776   Input Parameter:
5777 . dm - The `DMPLEX` object
5778 
5779   Output Parameter:
5780 . coneOrientations - The array of cone orientations for all points
5781 
5782   Level: developer
5783 
5784   Notes:
5785   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points
5786   as returned by `DMPlexGetConeOrientation()`.
5787 
5788   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5789 
5790 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5791 @*/
5792 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5793 {
5794   DM_Plex *mesh = (DM_Plex *)dm->data;
5795 
5796   PetscFunctionBegin;
5797   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5798   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5799   PetscFunctionReturn(PETSC_SUCCESS);
5800 }
5801 
5802 /* FEM Support */
5803 
5804 PetscErrorCode DMPlexGetAllCells_Internal(DM plex, IS *cellIS)
5805 {
5806   PetscInt depth;
5807 
5808   PetscFunctionBegin;
5809   PetscCall(DMPlexGetDepth(plex, &depth));
5810   PetscCall(DMGetStratumIS(plex, "dim", depth, cellIS));
5811   if (!*cellIS) PetscCall(DMGetStratumIS(plex, "depth", depth, cellIS));
5812   PetscFunctionReturn(PETSC_SUCCESS);
5813 }
5814 
5815 PetscErrorCode DMPlexGetAllFaces_Internal(DM plex, IS *faceIS)
5816 {
5817   PetscInt depth;
5818 
5819   PetscFunctionBegin;
5820   PetscCall(DMPlexGetDepth(plex, &depth));
5821   PetscCall(DMGetStratumIS(plex, "dim", depth - 1, faceIS));
5822   if (!*faceIS) PetscCall(DMGetStratumIS(plex, "depth", depth - 1, faceIS));
5823   PetscFunctionReturn(PETSC_SUCCESS);
5824 }
5825 
5826 /*
5827  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5828  representing a line in the section.
5829 */
5830 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(DM dm, PetscSection section, PetscInt field, PetscInt line, PetscInt *Nc, PetscInt *k, PetscBool *continuous, PetscBool *tensor)
5831 {
5832   PetscObject  obj;
5833   PetscClassId id;
5834   PetscFE      fe = NULL;
5835 
5836   PetscFunctionBeginHot;
5837   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5838   PetscCall(DMGetField(dm, field, NULL, &obj));
5839   PetscCall(PetscObjectGetClassId(obj, &id));
5840   if (id == PETSCFE_CLASSID) fe = (PetscFE)obj;
5841 
5842   if (!fe) {
5843     /* Assume the full interpolated mesh is in the chart; lines in particular */
5844     /* An order k SEM disc has k-1 dofs on an edge */
5845     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5846     *k = *k / *Nc + 1;
5847   } else {
5848     PetscInt       dual_space_size, dim;
5849     PetscDualSpace dsp;
5850 
5851     PetscCall(DMGetDimension(dm, &dim));
5852     PetscCall(PetscFEGetDualSpace(fe, &dsp));
5853     PetscCall(PetscDualSpaceGetDimension(dsp, &dual_space_size));
5854     *k = (PetscInt)PetscCeilReal(PetscPowReal(dual_space_size / *Nc, 1.0 / dim)) - 1;
5855     PetscCall(PetscDualSpaceLagrangeGetContinuity(dsp, continuous));
5856     PetscCall(PetscDualSpaceLagrangeGetTensor(dsp, tensor));
5857   }
5858   PetscFunctionReturn(PETSC_SUCCESS);
5859 }
5860 
5861 static PetscErrorCode GetFieldSize_Private(PetscInt dim, PetscInt k, PetscBool tensor, PetscInt *dof)
5862 {
5863   PetscFunctionBeginHot;
5864   if (tensor) {
5865     *dof = PetscPowInt(k + 1, dim);
5866   } else {
5867     switch (dim) {
5868     case 1:
5869       *dof = k + 1;
5870       break;
5871     case 2:
5872       *dof = ((k + 1) * (k + 2)) / 2;
5873       break;
5874     case 3:
5875       *dof = ((k + 1) * (k + 2) * (k + 3)) / 6;
5876       break;
5877     default:
5878       *dof = 0;
5879     }
5880   }
5881   PetscFunctionReturn(PETSC_SUCCESS);
5882 }
5883 
5884 /*@
5885   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5886   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5887   section provided (or the section of the `DM`).
5888 
5889   Input Parameters:
5890 + dm      - The `DM`
5891 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or `PETSC_DETERMINE`
5892 - section - The `PetscSection` to reorder, or `NULL` for the default section
5893 
5894   Example:
5895   A typical interpolated single-quad mesh might order points as
5896 .vb
5897   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5898 
5899   v4 -- e6 -- v3
5900   |           |
5901   e7    c0    e8
5902   |           |
5903   v1 -- e5 -- v2
5904 .ve
5905 
5906   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5907   dofs in the order of points, e.g.,
5908 .vb
5909     c0 -> [0,1,2,3]
5910     v1 -> [4]
5911     ...
5912     e5 -> [8, 9]
5913 .ve
5914 
5915   which corresponds to the dofs
5916 .vb
5917     6   10  11  7
5918     13  2   3   15
5919     12  0   1   14
5920     4   8   9   5
5921 .ve
5922 
5923   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5924 .vb
5925   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5926 .ve
5927 
5928   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5929 .vb
5930    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5931 .ve
5932 
5933   Level: developer
5934 
5935   Notes:
5936   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5937   degree of the basis.
5938 
5939   This is required to run with libCEED.
5940 
5941 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5942 @*/
5943 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5944 {
5945   DMLabel   label;
5946   PetscInt  dim, depth = -1, eStart = -1, Nf;
5947   PetscBool continuous = PETSC_TRUE, tensor = PETSC_TRUE;
5948 
5949   PetscFunctionBegin;
5950   PetscCall(DMGetDimension(dm, &dim));
5951   if (dim < 1) PetscFunctionReturn(PETSC_SUCCESS);
5952   if (point < 0) {
5953     PetscInt sStart, sEnd;
5954 
5955     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5956     point = sEnd - sStart ? sStart : point;
5957   }
5958   PetscCall(DMPlexGetDepthLabel(dm, &label));
5959   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5960   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5961   if (depth == 1) {
5962     eStart = point;
5963   } else if (depth == dim) {
5964     const PetscInt *cone;
5965 
5966     PetscCall(DMPlexGetCone(dm, point, &cone));
5967     if (dim == 2) eStart = cone[0];
5968     else if (dim == 3) {
5969       const PetscInt *cone2;
5970       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5971       eStart = cone2[0];
5972     } 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);
5973   } 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);
5974 
5975   PetscCall(PetscSectionGetNumFields(section, &Nf));
5976   for (PetscInt d = 1; d <= dim; d++) {
5977     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5978     PetscInt *perm;
5979 
5980     for (f = 0; f < Nf; ++f) {
5981       PetscInt dof;
5982 
5983       PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5984       PetscCheck(dim == 1 || tensor || !continuous, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Continuous field %" PetscInt_FMT " must have a tensor product discretization", f);
5985       if (!continuous && d < dim) continue;
5986       PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
5987       size += dof * Nc;
5988     }
5989     PetscCall(PetscMalloc1(size, &perm));
5990     for (f = 0; f < Nf; ++f) {
5991       switch (d) {
5992       case 1:
5993         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5994         if (!continuous && d < dim) continue;
5995         /*
5996          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5997          We want              [ vtx0; edge of length k-1; vtx1 ]
5998          */
5999         if (continuous) {
6000           for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
6001           for (i = 0; i < k - 1; i++)
6002             for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
6003           for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
6004           foffset = offset;
6005         } else {
6006           PetscInt dof;
6007 
6008           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6009           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6010           foffset = offset;
6011         }
6012         break;
6013       case 2:
6014         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
6015         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6016         if (!continuous && d < dim) continue;
6017         /* The SEM order is
6018 
6019          v_lb, {e_b}, v_rb,
6020          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
6021          v_lt, reverse {e_t}, v_rt
6022          */
6023         if (continuous) {
6024           const PetscInt of   = 0;
6025           const PetscInt oeb  = of + PetscSqr(k - 1);
6026           const PetscInt oer  = oeb + (k - 1);
6027           const PetscInt oet  = oer + (k - 1);
6028           const PetscInt oel  = oet + (k - 1);
6029           const PetscInt ovlb = oel + (k - 1);
6030           const PetscInt ovrb = ovlb + 1;
6031           const PetscInt ovrt = ovrb + 1;
6032           const PetscInt ovlt = ovrt + 1;
6033           PetscInt       o;
6034 
6035           /* bottom */
6036           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
6037           for (o = oeb; o < oer; ++o)
6038             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6039           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
6040           /* middle */
6041           for (i = 0; i < k - 1; ++i) {
6042             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
6043             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
6044               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6045             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
6046           }
6047           /* top */
6048           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
6049           for (o = oel - 1; o >= oet; --o)
6050             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6051           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
6052           foffset = offset;
6053         } else {
6054           PetscInt dof;
6055 
6056           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6057           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6058           foffset = offset;
6059         }
6060         break;
6061       case 3:
6062         /* The original hex closure is
6063 
6064          {c,
6065          f_b, f_t, f_f, f_b, f_r, f_l,
6066          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
6067          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
6068          */
6069         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6070         if (!continuous && d < dim) continue;
6071         /* The SEM order is
6072          Bottom Slice
6073          v_blf, {e^{(k-1)-n}_bf}, v_brf,
6074          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
6075          v_blb, {e_bb}, v_brb,
6076 
6077          Middle Slice (j)
6078          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
6079          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
6080          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
6081 
6082          Top Slice
6083          v_tlf, {e_tf}, v_trf,
6084          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
6085          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
6086          */
6087         if (continuous) {
6088           const PetscInt oc    = 0;
6089           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
6090           const PetscInt oft   = ofb + PetscSqr(k - 1);
6091           const PetscInt off   = oft + PetscSqr(k - 1);
6092           const PetscInt ofk   = off + PetscSqr(k - 1);
6093           const PetscInt ofr   = ofk + PetscSqr(k - 1);
6094           const PetscInt ofl   = ofr + PetscSqr(k - 1);
6095           const PetscInt oebl  = ofl + PetscSqr(k - 1);
6096           const PetscInt oebb  = oebl + (k - 1);
6097           const PetscInt oebr  = oebb + (k - 1);
6098           const PetscInt oebf  = oebr + (k - 1);
6099           const PetscInt oetf  = oebf + (k - 1);
6100           const PetscInt oetr  = oetf + (k - 1);
6101           const PetscInt oetb  = oetr + (k - 1);
6102           const PetscInt oetl  = oetb + (k - 1);
6103           const PetscInt oerf  = oetl + (k - 1);
6104           const PetscInt oelf  = oerf + (k - 1);
6105           const PetscInt oelb  = oelf + (k - 1);
6106           const PetscInt oerb  = oelb + (k - 1);
6107           const PetscInt ovblf = oerb + (k - 1);
6108           const PetscInt ovblb = ovblf + 1;
6109           const PetscInt ovbrb = ovblb + 1;
6110           const PetscInt ovbrf = ovbrb + 1;
6111           const PetscInt ovtlf = ovbrf + 1;
6112           const PetscInt ovtrf = ovtlf + 1;
6113           const PetscInt ovtrb = ovtrf + 1;
6114           const PetscInt ovtlb = ovtrb + 1;
6115           PetscInt       o, n;
6116 
6117           /* Bottom Slice */
6118           /*   bottom */
6119           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
6120           for (o = oetf - 1; o >= oebf; --o)
6121             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6122           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
6123           /*   middle */
6124           for (i = 0; i < k - 1; ++i) {
6125             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
6126             for (n = 0; n < k - 1; ++n) {
6127               o = ofb + n * (k - 1) + i;
6128               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6129             }
6130             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
6131           }
6132           /*   top */
6133           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
6134           for (o = oebb; o < oebr; ++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] = ovbrb * Nc + c + foffset;
6137 
6138           /* Middle Slice */
6139           for (j = 0; j < k - 1; ++j) {
6140             /*   bottom */
6141             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
6142             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
6143               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6144             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
6145             /*   middle */
6146             for (i = 0; i < k - 1; ++i) {
6147               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
6148               for (n = 0; n < k - 1; ++n)
6149                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
6150               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
6151             }
6152             /*   top */
6153             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
6154             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --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] = (oerb + (k - 2) - j) * Nc + c + foffset;
6157           }
6158 
6159           /* Top Slice */
6160           /*   bottom */
6161           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
6162           for (o = oetf; o < oetr; ++o)
6163             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6164           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
6165           /*   middle */
6166           for (i = 0; i < k - 1; ++i) {
6167             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
6168             for (n = 0; n < k - 1; ++n)
6169               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
6170             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
6171           }
6172           /*   top */
6173           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
6174           for (o = oetl - 1; o >= oetb; --o)
6175             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6176           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
6177 
6178           foffset = offset;
6179         } else {
6180           PetscInt dof;
6181 
6182           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6183           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6184           foffset = offset;
6185         }
6186         break;
6187       default:
6188         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
6189       }
6190     }
6191     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
6192     /* Check permutation */
6193     {
6194       PetscInt *check;
6195 
6196       PetscCall(PetscMalloc1(size, &check));
6197       for (i = 0; i < size; ++i) {
6198         check[i] = -1;
6199         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
6200       }
6201       for (i = 0; i < size; ++i) check[perm[i]] = i;
6202       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
6203       PetscCall(PetscFree(check));
6204     }
6205     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
6206     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
6207       PetscInt *loc_perm;
6208       PetscCall(PetscMalloc1(size * 2, &loc_perm));
6209       for (PetscInt i = 0; i < size; i++) {
6210         loc_perm[i]        = perm[i];
6211         loc_perm[size + i] = size + perm[i];
6212       }
6213       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
6214     }
6215   }
6216   PetscFunctionReturn(PETSC_SUCCESS);
6217 }
6218 
6219 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
6220 {
6221   PetscDS  prob;
6222   PetscInt depth, Nf, h;
6223   DMLabel  label;
6224 
6225   PetscFunctionBeginHot;
6226   PetscCall(DMGetDS(dm, &prob));
6227   Nf      = prob->Nf;
6228   label   = dm->depthLabel;
6229   *dspace = NULL;
6230   if (field < Nf) {
6231     PetscObject disc = prob->disc[field];
6232 
6233     if (disc->classid == PETSCFE_CLASSID) {
6234       PetscDualSpace dsp;
6235 
6236       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
6237       PetscCall(DMLabelGetNumValues(label, &depth));
6238       PetscCall(DMLabelGetValue(label, point, &h));
6239       h = depth - 1 - h;
6240       if (h) {
6241         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
6242       } else {
6243         *dspace = dsp;
6244       }
6245     }
6246   }
6247   PetscFunctionReturn(PETSC_SUCCESS);
6248 }
6249 
6250 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6251 {
6252   PetscScalar       *array;
6253   const PetscScalar *vArray;
6254   const PetscInt    *cone, *coneO;
6255   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
6256 
6257   PetscFunctionBeginHot;
6258   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6259   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6260   PetscCall(DMPlexGetCone(dm, point, &cone));
6261   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6262   if (!values || !*values) {
6263     if ((point >= pStart) && (point < pEnd)) {
6264       PetscInt dof;
6265 
6266       PetscCall(PetscSectionGetDof(section, point, &dof));
6267       size += dof;
6268     }
6269     for (p = 0; p < numPoints; ++p) {
6270       const PetscInt cp = cone[p];
6271       PetscInt       dof;
6272 
6273       if ((cp < pStart) || (cp >= pEnd)) continue;
6274       PetscCall(PetscSectionGetDof(section, cp, &dof));
6275       size += dof;
6276     }
6277     if (!values) {
6278       if (csize) *csize = size;
6279       PetscFunctionReturn(PETSC_SUCCESS);
6280     }
6281     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
6282   } else {
6283     array = *values;
6284   }
6285   size = 0;
6286   PetscCall(VecGetArrayRead(v, &vArray));
6287   if ((point >= pStart) && (point < pEnd)) {
6288     PetscInt           dof, off, d;
6289     const PetscScalar *varr;
6290 
6291     PetscCall(PetscSectionGetDof(section, point, &dof));
6292     PetscCall(PetscSectionGetOffset(section, point, &off));
6293     varr = PetscSafePointerPlusOffset(vArray, off);
6294     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6295     size += dof;
6296   }
6297   for (p = 0; p < numPoints; ++p) {
6298     const PetscInt     cp = cone[p];
6299     PetscInt           o  = coneO[p];
6300     PetscInt           dof, off, d;
6301     const PetscScalar *varr;
6302 
6303     if ((cp < pStart) || (cp >= pEnd)) continue;
6304     PetscCall(PetscSectionGetDof(section, cp, &dof));
6305     PetscCall(PetscSectionGetOffset(section, cp, &off));
6306     varr = PetscSafePointerPlusOffset(vArray, off);
6307     if (o >= 0) {
6308       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6309     } else {
6310       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
6311     }
6312     size += dof;
6313   }
6314   PetscCall(VecRestoreArrayRead(v, &vArray));
6315   if (!*values) {
6316     if (csize) *csize = size;
6317     *values = array;
6318   } else {
6319     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6320     *csize = size;
6321   }
6322   PetscFunctionReturn(PETSC_SUCCESS);
6323 }
6324 
6325 /* Compress out points not in the section */
6326 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
6327 {
6328   const PetscInt np = *numPoints;
6329   PetscInt       pStart, pEnd, p, q;
6330 
6331   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6332   for (p = 0, q = 0; p < np; ++p) {
6333     const PetscInt r = points[p * 2];
6334     if ((r >= pStart) && (r < pEnd)) {
6335       points[q * 2]     = r;
6336       points[q * 2 + 1] = points[p * 2 + 1];
6337       ++q;
6338     }
6339   }
6340   *numPoints = q;
6341   return PETSC_SUCCESS;
6342 }
6343 
6344 /* Compressed closure does not apply closure permutation */
6345 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt ornt, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6346 {
6347   const PetscInt *cla = NULL;
6348   PetscInt        np, *pts = NULL;
6349 
6350   PetscFunctionBeginHot;
6351   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
6352   if (!ornt && *clPoints) {
6353     PetscInt dof, off;
6354 
6355     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
6356     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
6357     PetscCall(ISGetIndices(*clPoints, &cla));
6358     np  = dof / 2;
6359     pts = PetscSafePointerPlusOffset((PetscInt *)cla, off);
6360   } else {
6361     PetscCall(DMPlexGetTransitiveClosure_Internal(dm, point, ornt, PETSC_TRUE, &np, &pts));
6362     PetscCall(CompressPoints_Private(section, &np, pts));
6363   }
6364   *numPoints = np;
6365   *points    = pts;
6366   *clp       = cla;
6367   PetscFunctionReturn(PETSC_SUCCESS);
6368 }
6369 
6370 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6371 {
6372   PetscFunctionBeginHot;
6373   if (!*clPoints) {
6374     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
6375   } else {
6376     PetscCall(ISRestoreIndices(*clPoints, clp));
6377   }
6378   *numPoints = 0;
6379   *points    = NULL;
6380   *clSec     = NULL;
6381   *clPoints  = NULL;
6382   *clp       = NULL;
6383   PetscFunctionReturn(PETSC_SUCCESS);
6384 }
6385 
6386 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
6387 {
6388   PetscInt            offset = 0, p;
6389   const PetscInt    **perms  = NULL;
6390   const PetscScalar **flips  = NULL;
6391 
6392   PetscFunctionBeginHot;
6393   *size = 0;
6394   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6395   for (p = 0; p < numPoints; p++) {
6396     const PetscInt     point = points[2 * p];
6397     const PetscInt    *perm  = perms ? perms[p] : NULL;
6398     const PetscScalar *flip  = flips ? flips[p] : NULL;
6399     PetscInt           dof, off, d;
6400     const PetscScalar *varr;
6401 
6402     PetscCall(PetscSectionGetDof(section, point, &dof));
6403     PetscCall(PetscSectionGetOffset(section, point, &off));
6404     varr = PetscSafePointerPlusOffset(vArray, off);
6405     if (clperm) {
6406       if (perm) {
6407         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
6408       } else {
6409         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
6410       }
6411       if (flip) {
6412         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
6413       }
6414     } else {
6415       if (perm) {
6416         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
6417       } else {
6418         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
6419       }
6420       if (flip) {
6421         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
6422       }
6423     }
6424     offset += dof;
6425   }
6426   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6427   *size = offset;
6428   PetscFunctionReturn(PETSC_SUCCESS);
6429 }
6430 
6431 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[])
6432 {
6433   PetscInt offset = 0, f;
6434 
6435   PetscFunctionBeginHot;
6436   *size = 0;
6437   for (f = 0; f < numFields; ++f) {
6438     PetscInt            p;
6439     const PetscInt    **perms = NULL;
6440     const PetscScalar **flips = NULL;
6441 
6442     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6443     for (p = 0; p < numPoints; p++) {
6444       const PetscInt     point = points[2 * p];
6445       PetscInt           fdof, foff, b;
6446       const PetscScalar *varr;
6447       const PetscInt    *perm = perms ? perms[p] : NULL;
6448       const PetscScalar *flip = flips ? flips[p] : NULL;
6449 
6450       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6451       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6452       varr = &vArray[foff];
6453       if (clperm) {
6454         if (perm) {
6455           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
6456         } else {
6457           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
6458         }
6459         if (flip) {
6460           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
6461         }
6462       } else {
6463         if (perm) {
6464           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
6465         } else {
6466           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
6467         }
6468         if (flip) {
6469           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
6470         }
6471       }
6472       offset += fdof;
6473     }
6474     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6475   }
6476   *size = offset;
6477   PetscFunctionReturn(PETSC_SUCCESS);
6478 }
6479 
6480 PetscErrorCode DMPlexVecGetOrientedClosure_Internal(DM dm, PetscSection section, PetscBool useClPerm, Vec v, PetscInt point, PetscInt ornt, PetscInt *csize, PetscScalar *values[])
6481 {
6482   PetscSection    clSection;
6483   IS              clPoints;
6484   PetscInt       *points = NULL;
6485   const PetscInt *clp, *perm = NULL;
6486   PetscInt        depth, numFields, numPoints, asize;
6487 
6488   PetscFunctionBeginHot;
6489   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6490   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6491   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6492   PetscValidHeaderSpecific(v, VEC_CLASSID, 4);
6493   PetscCall(DMPlexGetDepth(dm, &depth));
6494   PetscCall(PetscSectionGetNumFields(section, &numFields));
6495   if (depth == 1 && numFields < 2) {
6496     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6497     PetscFunctionReturn(PETSC_SUCCESS);
6498   }
6499   /* Get points */
6500   PetscCall(DMPlexGetCompressedClosure(dm, section, point, ornt, &numPoints, &points, &clSection, &clPoints, &clp));
6501   /* Get sizes */
6502   asize = 0;
6503   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6504     PetscInt dof;
6505     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6506     asize += dof;
6507   }
6508   if (values) {
6509     const PetscScalar *vArray;
6510     PetscInt           size;
6511 
6512     if (*values) {
6513       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);
6514     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6515     if (useClPerm) PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6516     PetscCall(VecGetArrayRead(v, &vArray));
6517     /* Get values */
6518     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6519     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6520     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6521     /* Cleanup array */
6522     PetscCall(VecRestoreArrayRead(v, &vArray));
6523   }
6524   if (csize) *csize = asize;
6525   /* Cleanup points */
6526   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6527   PetscFunctionReturn(PETSC_SUCCESS);
6528 }
6529 
6530 /*@C
6531   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
6532 
6533   Not collective
6534 
6535   Input Parameters:
6536 + dm      - The `DM`
6537 . section - The section describing the layout in `v`, or `NULL` to use the default section
6538 . v       - The local vector
6539 - point   - The point in the `DM`
6540 
6541   Input/Output Parameters:
6542 + csize  - The size of the input values array, or `NULL`; on output the number of values in the closure
6543 - values - An array to use for the values, or *values = `NULL` to have it allocated automatically;
6544            if the user provided `NULL`, it is a borrowed array and should not be freed, use  `DMPlexVecRestoreClosure()` to return it
6545 
6546   Level: intermediate
6547 
6548   Notes:
6549   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6550   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
6551   assembly function, and a user may already have allocated storage for this operation.
6552 
6553   A typical use could be
6554 .vb
6555    values = NULL;
6556    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6557    for (cl = 0; cl < clSize; ++cl) {
6558      <Compute on closure>
6559    }
6560    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
6561 .ve
6562   or
6563 .vb
6564    PetscMalloc1(clMaxSize, &values);
6565    for (p = pStart; p < pEnd; ++p) {
6566      clSize = clMaxSize;
6567      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6568      for (cl = 0; cl < clSize; ++cl) {
6569        <Compute on closure>
6570      }
6571    }
6572    PetscFree(values);
6573 .ve
6574 
6575   Fortran Notes:
6576   The `csize` argument is not present in the Fortran binding.
6577 
6578   `values` must be declared with
6579 .vb
6580   PetscScalar,dimension(:),pointer   :: values
6581 .ve
6582   and it will be allocated internally by PETSc to hold the values returned
6583 
6584 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6585 @*/
6586 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6587 {
6588   PetscFunctionBeginHot;
6589   PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, section, PETSC_TRUE, v, point, 0, csize, values));
6590   PetscFunctionReturn(PETSC_SUCCESS);
6591 }
6592 
6593 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6594 {
6595   DMLabel            depthLabel;
6596   PetscSection       clSection;
6597   IS                 clPoints;
6598   PetscScalar       *array;
6599   const PetscScalar *vArray;
6600   PetscInt          *points = NULL;
6601   const PetscInt    *clp, *perm = NULL;
6602   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6603 
6604   PetscFunctionBeginHot;
6605   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6606   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6607   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6608   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6609   PetscCall(DMPlexGetDepth(dm, &mdepth));
6610   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6611   PetscCall(PetscSectionGetNumFields(section, &numFields));
6612   if (mdepth == 1 && numFields < 2) {
6613     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6614     PetscFunctionReturn(PETSC_SUCCESS);
6615   }
6616   /* Get points */
6617   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6618   for (clsize = 0, p = 0; p < Np; p++) {
6619     PetscInt dof;
6620     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6621     clsize += dof;
6622   }
6623   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6624   /* Filter points */
6625   for (p = 0; p < numPoints * 2; p += 2) {
6626     PetscInt dep;
6627 
6628     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6629     if (dep != depth) continue;
6630     points[Np * 2 + 0] = points[p];
6631     points[Np * 2 + 1] = points[p + 1];
6632     ++Np;
6633   }
6634   /* Get array */
6635   if (!values || !*values) {
6636     PetscInt asize = 0, dof;
6637 
6638     for (p = 0; p < Np * 2; p += 2) {
6639       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6640       asize += dof;
6641     }
6642     if (!values) {
6643       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6644       if (csize) *csize = asize;
6645       PetscFunctionReturn(PETSC_SUCCESS);
6646     }
6647     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6648   } else {
6649     array = *values;
6650   }
6651   PetscCall(VecGetArrayRead(v, &vArray));
6652   /* Get values */
6653   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6654   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6655   /* Cleanup points */
6656   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6657   /* Cleanup array */
6658   PetscCall(VecRestoreArrayRead(v, &vArray));
6659   if (!*values) {
6660     if (csize) *csize = size;
6661     *values = array;
6662   } else {
6663     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6664     *csize = size;
6665   }
6666   PetscFunctionReturn(PETSC_SUCCESS);
6667 }
6668 
6669 /*@C
6670   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point' obtained with `DMPlexVecGetClosure()`
6671 
6672   Not collective
6673 
6674   Input Parameters:
6675 + dm      - The `DM`
6676 . section - The section describing the layout in `v`, or `NULL` to use the default section
6677 . v       - The local vector
6678 . point   - The point in the `DM`
6679 . csize   - The number of values in the closure, or `NULL`
6680 - values  - The array of values
6681 
6682   Level: intermediate
6683 
6684   Note:
6685   The array values are discarded and not copied back into `v`. In order to copy values back to `v`, use `DMPlexVecSetClosure()`
6686 
6687   Fortran Note:
6688   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6689 
6690 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6691 @*/
6692 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6693 {
6694   PetscInt size = 0;
6695 
6696   PetscFunctionBegin;
6697   /* Should work without recalculating size */
6698   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6699   *values = NULL;
6700   PetscFunctionReturn(PETSC_SUCCESS);
6701 }
6702 
6703 static inline void add(PetscScalar *x, PetscScalar y)
6704 {
6705   *x += y;
6706 }
6707 static inline void insert(PetscScalar *x, PetscScalar y)
6708 {
6709   *x = y;
6710 }
6711 
6712 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[])
6713 {
6714   PetscInt        cdof;  /* The number of constraints on this point */
6715   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6716   PetscScalar    *a;
6717   PetscInt        off, cind = 0, k;
6718 
6719   PetscFunctionBegin;
6720   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6721   PetscCall(PetscSectionGetOffset(section, point, &off));
6722   a = &array[off];
6723   if (!cdof || setBC) {
6724     if (clperm) {
6725       if (perm) {
6726         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6727       } else {
6728         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6729       }
6730     } else {
6731       if (perm) {
6732         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6733       } else {
6734         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6735       }
6736     }
6737   } else {
6738     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6739     if (clperm) {
6740       if (perm) {
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 + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6747         }
6748       } else {
6749         for (k = 0; k < dof; ++k) {
6750           if ((cind < cdof) && (k == cdofs[cind])) {
6751             ++cind;
6752             continue;
6753           }
6754           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6755         }
6756       }
6757     } else {
6758       if (perm) {
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 + perm[k]] * (flip ? flip[perm[k]] : 1.));
6765         }
6766       } else {
6767         for (k = 0; k < dof; ++k) {
6768           if ((cind < cdof) && (k == cdofs[cind])) {
6769             ++cind;
6770             continue;
6771           }
6772           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6773         }
6774       }
6775     }
6776   }
6777   PetscFunctionReturn(PETSC_SUCCESS);
6778 }
6779 
6780 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[])
6781 {
6782   PetscInt        cdof;  /* The number of constraints on this point */
6783   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6784   PetscScalar    *a;
6785   PetscInt        off, cind = 0, k;
6786 
6787   PetscFunctionBegin;
6788   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6789   PetscCall(PetscSectionGetOffset(section, point, &off));
6790   a = &array[off];
6791   if (cdof) {
6792     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6793     if (clperm) {
6794       if (perm) {
6795         for (k = 0; k < dof; ++k) {
6796           if ((cind < cdof) && (k == cdofs[cind])) {
6797             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6798             cind++;
6799           }
6800         }
6801       } else {
6802         for (k = 0; k < dof; ++k) {
6803           if ((cind < cdof) && (k == cdofs[cind])) {
6804             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6805             cind++;
6806           }
6807         }
6808       }
6809     } else {
6810       if (perm) {
6811         for (k = 0; k < dof; ++k) {
6812           if ((cind < cdof) && (k == cdofs[cind])) {
6813             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6814             cind++;
6815           }
6816         }
6817       } else {
6818         for (k = 0; k < dof; ++k) {
6819           if ((cind < cdof) && (k == cdofs[cind])) {
6820             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6821             cind++;
6822           }
6823         }
6824       }
6825     }
6826   }
6827   PetscFunctionReturn(PETSC_SUCCESS);
6828 }
6829 
6830 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[])
6831 {
6832   PetscScalar    *a;
6833   PetscInt        fdof, foff, fcdof, foffset = *offset;
6834   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6835   PetscInt        cind = 0, b;
6836 
6837   PetscFunctionBegin;
6838   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6839   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6840   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6841   a = &array[foff];
6842   if (!fcdof || setBC) {
6843     if (clperm) {
6844       if (perm) {
6845         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6846       } else {
6847         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6848       }
6849     } else {
6850       if (perm) {
6851         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6852       } else {
6853         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6854       }
6855     }
6856   } else {
6857     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6858     if (clperm) {
6859       if (perm) {
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 + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6866         }
6867       } else {
6868         for (b = 0; b < fdof; b++) {
6869           if ((cind < fcdof) && (b == fcdofs[cind])) {
6870             ++cind;
6871             continue;
6872           }
6873           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6874         }
6875       }
6876     } else {
6877       if (perm) {
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 + perm[b]] * (flip ? flip[perm[b]] : 1.));
6884         }
6885       } else {
6886         for (b = 0; b < fdof; b++) {
6887           if ((cind < fcdof) && (b == fcdofs[cind])) {
6888             ++cind;
6889             continue;
6890           }
6891           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6892         }
6893       }
6894     }
6895   }
6896   *offset += fdof;
6897   PetscFunctionReturn(PETSC_SUCCESS);
6898 }
6899 
6900 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[])
6901 {
6902   PetscScalar    *a;
6903   PetscInt        fdof, foff, fcdof, foffset = *offset;
6904   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6905   PetscInt        Nc, cind = 0, ncind = 0, b;
6906   PetscBool       ncSet, fcSet;
6907 
6908   PetscFunctionBegin;
6909   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6910   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6911   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6912   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6913   a = &array[foff];
6914   if (fcdof) {
6915     /* We just override fcdof and fcdofs with Ncc and comps */
6916     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6917     if (clperm) {
6918       if (perm) {
6919         if (comps) {
6920           for (b = 0; b < fdof; b++) {
6921             ncSet = fcSet = PETSC_FALSE;
6922             if (b % Nc == comps[ncind]) {
6923               ncind = (ncind + 1) % Ncc;
6924               ncSet = PETSC_TRUE;
6925             }
6926             if ((cind < fcdof) && (b == fcdofs[cind])) {
6927               ++cind;
6928               fcSet = PETSC_TRUE;
6929             }
6930             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6931           }
6932         } else {
6933           for (b = 0; b < fdof; b++) {
6934             if ((cind < fcdof) && (b == fcdofs[cind])) {
6935               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6936               ++cind;
6937             }
6938           }
6939         }
6940       } else {
6941         if (comps) {
6942           for (b = 0; b < fdof; b++) {
6943             ncSet = fcSet = PETSC_FALSE;
6944             if (b % Nc == comps[ncind]) {
6945               ncind = (ncind + 1) % Ncc;
6946               ncSet = PETSC_TRUE;
6947             }
6948             if ((cind < fcdof) && (b == fcdofs[cind])) {
6949               ++cind;
6950               fcSet = PETSC_TRUE;
6951             }
6952             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6953           }
6954         } else {
6955           for (b = 0; b < fdof; b++) {
6956             if ((cind < fcdof) && (b == fcdofs[cind])) {
6957               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6958               ++cind;
6959             }
6960           }
6961         }
6962       }
6963     } else {
6964       if (perm) {
6965         if (comps) {
6966           for (b = 0; b < fdof; b++) {
6967             ncSet = fcSet = PETSC_FALSE;
6968             if (b % Nc == comps[ncind]) {
6969               ncind = (ncind + 1) % Ncc;
6970               ncSet = PETSC_TRUE;
6971             }
6972             if ((cind < fcdof) && (b == fcdofs[cind])) {
6973               ++cind;
6974               fcSet = PETSC_TRUE;
6975             }
6976             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6977           }
6978         } else {
6979           for (b = 0; b < fdof; b++) {
6980             if ((cind < fcdof) && (b == fcdofs[cind])) {
6981               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6982               ++cind;
6983             }
6984           }
6985         }
6986       } else {
6987         if (comps) {
6988           for (b = 0; b < fdof; b++) {
6989             ncSet = fcSet = PETSC_FALSE;
6990             if (b % Nc == comps[ncind]) {
6991               ncind = (ncind + 1) % Ncc;
6992               ncSet = PETSC_TRUE;
6993             }
6994             if ((cind < fcdof) && (b == fcdofs[cind])) {
6995               ++cind;
6996               fcSet = PETSC_TRUE;
6997             }
6998             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6999           }
7000         } else {
7001           for (b = 0; b < fdof; b++) {
7002             if ((cind < fcdof) && (b == fcdofs[cind])) {
7003               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
7004               ++cind;
7005             }
7006           }
7007         }
7008       }
7009     }
7010   }
7011   *offset += fdof;
7012   PetscFunctionReturn(PETSC_SUCCESS);
7013 }
7014 
7015 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
7016 {
7017   PetscScalar    *array;
7018   const PetscInt *cone, *coneO;
7019   PetscInt        pStart, pEnd, p, numPoints, off, dof;
7020 
7021   PetscFunctionBeginHot;
7022   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
7023   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
7024   PetscCall(DMPlexGetCone(dm, point, &cone));
7025   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
7026   PetscCall(VecGetArray(v, &array));
7027   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
7028     const PetscInt cp = !p ? point : cone[p - 1];
7029     const PetscInt o  = !p ? 0 : coneO[p - 1];
7030 
7031     if ((cp < pStart) || (cp >= pEnd)) {
7032       dof = 0;
7033       continue;
7034     }
7035     PetscCall(PetscSectionGetDof(section, cp, &dof));
7036     /* ADD_VALUES */
7037     {
7038       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7039       PetscScalar    *a;
7040       PetscInt        cdof, coff, cind = 0, k;
7041 
7042       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
7043       PetscCall(PetscSectionGetOffset(section, cp, &coff));
7044       a = &array[coff];
7045       if (!cdof) {
7046         if (o >= 0) {
7047           for (k = 0; k < dof; ++k) a[k] += values[off + k];
7048         } else {
7049           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
7050         }
7051       } else {
7052         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
7053         if (o >= 0) {
7054           for (k = 0; k < dof; ++k) {
7055             if ((cind < cdof) && (k == cdofs[cind])) {
7056               ++cind;
7057               continue;
7058             }
7059             a[k] += values[off + k];
7060           }
7061         } else {
7062           for (k = 0; k < dof; ++k) {
7063             if ((cind < cdof) && (k == cdofs[cind])) {
7064               ++cind;
7065               continue;
7066             }
7067             a[k] += values[off + dof - k - 1];
7068           }
7069         }
7070       }
7071     }
7072   }
7073   PetscCall(VecRestoreArray(v, &array));
7074   PetscFunctionReturn(PETSC_SUCCESS);
7075 }
7076 
7077 /*@C
7078   DMPlexVecSetClosure - Set an array of the values on the closure of `point`
7079 
7080   Not collective
7081 
7082   Input Parameters:
7083 + dm      - The `DM`
7084 . section - The section describing the layout in `v`, or `NULL` to use the default section
7085 . v       - The local vector
7086 . point   - The point in the `DM`
7087 . values  - The array of values
7088 - mode    - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
7089             where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
7090 
7091   Level: intermediate
7092 
7093   Note:
7094   Usually the input arrays were obtained with `DMPlexVecGetClosure()`
7095 
7096   Fortran Note:
7097   `values` must be declared with
7098 .vb
7099   PetscScalar,dimension(:),pointer   :: values
7100 .ve
7101 
7102 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
7103 @*/
7104 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
7105 {
7106   PetscSection    clSection;
7107   IS              clPoints;
7108   PetscScalar    *array;
7109   PetscInt       *points = NULL;
7110   const PetscInt *clp, *clperm = NULL;
7111   PetscInt        depth, numFields, numPoints, p, clsize;
7112 
7113   PetscFunctionBeginHot;
7114   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7115   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7116   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7117   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7118   PetscCall(DMPlexGetDepth(dm, &depth));
7119   PetscCall(PetscSectionGetNumFields(section, &numFields));
7120   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
7121     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
7122     PetscFunctionReturn(PETSC_SUCCESS);
7123   }
7124   /* Get points */
7125   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7126   for (clsize = 0, p = 0; p < numPoints; p++) {
7127     PetscInt dof;
7128     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7129     clsize += dof;
7130   }
7131   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7132   /* Get array */
7133   PetscCall(VecGetArray(v, &array));
7134   /* Get values */
7135   if (numFields > 0) {
7136     PetscInt offset = 0, f;
7137     for (f = 0; f < numFields; ++f) {
7138       const PetscInt    **perms = NULL;
7139       const PetscScalar **flips = NULL;
7140 
7141       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7142       switch (mode) {
7143       case INSERT_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_FALSE, clperm, values, &offset, array));
7149         }
7150         break;
7151       case INSERT_ALL_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(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array));
7157         }
7158         break;
7159       case INSERT_BC_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(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array));
7165         }
7166         break;
7167       case ADD_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_FALSE, clperm, values, &offset, array));
7173         }
7174         break;
7175       case ADD_ALL_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(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array));
7181         }
7182         break;
7183       case ADD_BC_VALUES:
7184         for (p = 0; p < numPoints; p++) {
7185           const PetscInt     point = points[2 * p];
7186           const PetscInt    *perm  = perms ? perms[p] : NULL;
7187           const PetscScalar *flip  = flips ? flips[p] : NULL;
7188           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array));
7189         }
7190         break;
7191       default:
7192         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7193       }
7194       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7195     }
7196   } else {
7197     PetscInt            dof, off;
7198     const PetscInt    **perms = NULL;
7199     const PetscScalar **flips = NULL;
7200 
7201     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
7202     switch (mode) {
7203     case INSERT_VALUES:
7204       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7205         const PetscInt     point = points[2 * p];
7206         const PetscInt    *perm  = perms ? perms[p] : NULL;
7207         const PetscScalar *flip  = flips ? flips[p] : NULL;
7208         PetscCall(PetscSectionGetDof(section, point, &dof));
7209         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array));
7210       }
7211       break;
7212     case INSERT_ALL_VALUES:
7213       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7214         const PetscInt     point = points[2 * p];
7215         const PetscInt    *perm  = perms ? perms[p] : NULL;
7216         const PetscScalar *flip  = flips ? flips[p] : NULL;
7217         PetscCall(PetscSectionGetDof(section, point, &dof));
7218         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array));
7219       }
7220       break;
7221     case INSERT_BC_VALUES:
7222       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7223         const PetscInt     point = points[2 * p];
7224         const PetscInt    *perm  = perms ? perms[p] : NULL;
7225         const PetscScalar *flip  = flips ? flips[p] : NULL;
7226         PetscCall(PetscSectionGetDof(section, point, &dof));
7227         PetscCall(updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array));
7228       }
7229       break;
7230     case ADD_VALUES:
7231       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7232         const PetscInt     point = points[2 * p];
7233         const PetscInt    *perm  = perms ? perms[p] : NULL;
7234         const PetscScalar *flip  = flips ? flips[p] : NULL;
7235         PetscCall(PetscSectionGetDof(section, point, &dof));
7236         PetscCall(updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array));
7237       }
7238       break;
7239     case ADD_ALL_VALUES:
7240       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7241         const PetscInt     point = points[2 * p];
7242         const PetscInt    *perm  = perms ? perms[p] : NULL;
7243         const PetscScalar *flip  = flips ? flips[p] : NULL;
7244         PetscCall(PetscSectionGetDof(section, point, &dof));
7245         PetscCall(updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array));
7246       }
7247       break;
7248     case ADD_BC_VALUES:
7249       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7250         const PetscInt     point = points[2 * p];
7251         const PetscInt    *perm  = perms ? perms[p] : NULL;
7252         const PetscScalar *flip  = flips ? flips[p] : NULL;
7253         PetscCall(PetscSectionGetDof(section, point, &dof));
7254         PetscCall(updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array));
7255       }
7256       break;
7257     default:
7258       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7259     }
7260     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
7261   }
7262   /* Cleanup points */
7263   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7264   /* Cleanup array */
7265   PetscCall(VecRestoreArray(v, &array));
7266   PetscFunctionReturn(PETSC_SUCCESS);
7267 }
7268 
7269 /* Check whether the given point is in the label. If not, update the offset to skip this point */
7270 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
7271 {
7272   PetscFunctionBegin;
7273   *contains = PETSC_TRUE;
7274   if (label) {
7275     PetscInt fdof;
7276 
7277     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
7278     if (!*contains) {
7279       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7280       *offset += fdof;
7281       PetscFunctionReturn(PETSC_SUCCESS);
7282     }
7283   }
7284   PetscFunctionReturn(PETSC_SUCCESS);
7285 }
7286 
7287 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
7288 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)
7289 {
7290   PetscSection    clSection;
7291   IS              clPoints;
7292   PetscScalar    *array;
7293   PetscInt       *points = NULL;
7294   const PetscInt *clp;
7295   PetscInt        numFields, numPoints, p;
7296   PetscInt        offset = 0, f;
7297 
7298   PetscFunctionBeginHot;
7299   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7300   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7301   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7302   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7303   PetscCall(PetscSectionGetNumFields(section, &numFields));
7304   /* Get points */
7305   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7306   /* Get array */
7307   PetscCall(VecGetArray(v, &array));
7308   /* Get values */
7309   for (f = 0; f < numFields; ++f) {
7310     const PetscInt    **perms = NULL;
7311     const PetscScalar **flips = NULL;
7312     PetscBool           contains;
7313 
7314     if (!fieldActive[f]) {
7315       for (p = 0; p < numPoints * 2; p += 2) {
7316         PetscInt fdof;
7317         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7318         offset += fdof;
7319       }
7320       continue;
7321     }
7322     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7323     switch (mode) {
7324     case INSERT_VALUES:
7325       for (p = 0; p < numPoints; p++) {
7326         const PetscInt     point = points[2 * p];
7327         const PetscInt    *perm  = perms ? perms[p] : NULL;
7328         const PetscScalar *flip  = flips ? flips[p] : NULL;
7329         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7330         if (!contains) continue;
7331         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
7332       }
7333       break;
7334     case INSERT_ALL_VALUES:
7335       for (p = 0; p < numPoints; p++) {
7336         const PetscInt     point = points[2 * p];
7337         const PetscInt    *perm  = perms ? perms[p] : NULL;
7338         const PetscScalar *flip  = flips ? flips[p] : NULL;
7339         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7340         if (!contains) continue;
7341         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
7342       }
7343       break;
7344     case INSERT_BC_VALUES:
7345       for (p = 0; p < numPoints; p++) {
7346         const PetscInt     point = points[2 * p];
7347         const PetscInt    *perm  = perms ? perms[p] : NULL;
7348         const PetscScalar *flip  = flips ? flips[p] : NULL;
7349         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7350         if (!contains) continue;
7351         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
7352       }
7353       break;
7354     case ADD_VALUES:
7355       for (p = 0; p < numPoints; p++) {
7356         const PetscInt     point = points[2 * p];
7357         const PetscInt    *perm  = perms ? perms[p] : NULL;
7358         const PetscScalar *flip  = flips ? flips[p] : NULL;
7359         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7360         if (!contains) continue;
7361         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
7362       }
7363       break;
7364     case ADD_ALL_VALUES:
7365       for (p = 0; p < numPoints; p++) {
7366         const PetscInt     point = points[2 * p];
7367         const PetscInt    *perm  = perms ? perms[p] : NULL;
7368         const PetscScalar *flip  = flips ? flips[p] : NULL;
7369         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7370         if (!contains) continue;
7371         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
7372       }
7373       break;
7374     default:
7375       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7376     }
7377     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7378   }
7379   /* Cleanup points */
7380   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7381   /* Cleanup array */
7382   PetscCall(VecRestoreArray(v, &array));
7383   PetscFunctionReturn(PETSC_SUCCESS);
7384 }
7385 
7386 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
7387 {
7388   PetscMPIInt rank;
7389   PetscInt    i, j;
7390 
7391   PetscFunctionBegin;
7392   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7393   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
7394   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
7395   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
7396   numCIndices = numCIndices ? numCIndices : numRIndices;
7397   if (!values) PetscFunctionReturn(PETSC_SUCCESS);
7398   for (i = 0; i < numRIndices; i++) {
7399     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
7400     for (j = 0; j < numCIndices; j++) {
7401 #if defined(PETSC_USE_COMPLEX)
7402       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
7403 #else
7404       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
7405 #endif
7406     }
7407     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
7408   }
7409   PetscFunctionReturn(PETSC_SUCCESS);
7410 }
7411 
7412 /*
7413   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
7414 
7415   Input Parameters:
7416 + section - The section for this data layout
7417 . islocal - Is the section (and thus indices being requested) local or global?
7418 . point   - The point contributing dofs with these indices
7419 . off     - The global offset of this point
7420 . loff    - The local offset of each field
7421 . setBC   - The flag determining whether to include indices of boundary values
7422 . perm    - A permutation of the dofs on this point, or NULL
7423 - indperm - A permutation of the entire indices array, or NULL
7424 
7425   Output Parameter:
7426 . indices - Indices for dofs on this point
7427 
7428   Level: developer
7429 
7430   Note: The indices could be local or global, depending on the value of 'off'.
7431 */
7432 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
7433 {
7434   PetscInt        dof;   /* The number of unknowns on this point */
7435   PetscInt        cdof;  /* The number of constraints on this point */
7436   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7437   PetscInt        cind = 0, k;
7438 
7439   PetscFunctionBegin;
7440   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7441   PetscCall(PetscSectionGetDof(section, point, &dof));
7442   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
7443   if (!cdof || setBC) {
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       indices[ind] = off + k;
7449     }
7450   } else {
7451     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
7452     for (k = 0; k < dof; ++k) {
7453       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7454       const PetscInt ind    = indperm ? indperm[preind] : preind;
7455 
7456       if ((cind < cdof) && (k == cdofs[cind])) {
7457         /* Insert check for returning constrained indices */
7458         indices[ind] = -(off + k + 1);
7459         ++cind;
7460       } else {
7461         indices[ind] = off + k - (islocal ? 0 : cind);
7462       }
7463     }
7464   }
7465   *loff += dof;
7466   PetscFunctionReturn(PETSC_SUCCESS);
7467 }
7468 
7469 /*
7470  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
7471 
7472  Input Parameters:
7473 + section - a section (global or local)
7474 - islocal - `PETSC_TRUE` if requesting local indices (i.e., section is local); `PETSC_FALSE` for global
7475 . point - point within section
7476 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
7477 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
7478 . setBC - identify constrained (boundary condition) points via involution.
7479 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
7480 . permsoff - offset
7481 - indperm - index permutation
7482 
7483  Output Parameter:
7484 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
7485 . indices - array to hold indices (as defined by section) of each dof associated with point
7486 
7487  Notes:
7488  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
7489  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
7490  in the local vector.
7491 
7492  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
7493  significant).  It is invalid to call with a global section and setBC=true.
7494 
7495  Developer Note:
7496  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
7497  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
7498  offset could be obtained from the section instead of passing it explicitly as we do now.
7499 
7500  Example:
7501  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
7502  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
7503  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
7504  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.
7505 
7506  Level: developer
7507 */
7508 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[])
7509 {
7510   PetscInt numFields, foff, f;
7511 
7512   PetscFunctionBegin;
7513   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7514   PetscCall(PetscSectionGetNumFields(section, &numFields));
7515   for (f = 0, foff = 0; f < numFields; ++f) {
7516     PetscInt        fdof, cfdof;
7517     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7518     PetscInt        cind = 0, b;
7519     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7520 
7521     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7522     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7523     if (!cfdof || setBC) {
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         indices[ind] = off + foff + b;
7529       }
7530     } else {
7531       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7532       for (b = 0; b < fdof; ++b) {
7533         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7534         const PetscInt ind    = indperm ? indperm[preind] : preind;
7535 
7536         if ((cind < cfdof) && (b == fcdofs[cind])) {
7537           indices[ind] = -(off + foff + b + 1);
7538           ++cind;
7539         } else {
7540           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7541         }
7542       }
7543     }
7544     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7545     foffs[f] += fdof;
7546   }
7547   PetscFunctionReturn(PETSC_SUCCESS);
7548 }
7549 
7550 /*
7551   This version believes the globalSection offsets for each field, rather than just the point offset
7552 
7553  . foffs - The offset into 'indices' for each field, since it is segregated by field
7554 
7555  Notes:
7556  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7557  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7558 */
7559 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7560 {
7561   PetscInt numFields, foff, f;
7562 
7563   PetscFunctionBegin;
7564   PetscCall(PetscSectionGetNumFields(section, &numFields));
7565   for (f = 0; f < numFields; ++f) {
7566     PetscInt        fdof, cfdof;
7567     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7568     PetscInt        cind = 0, b;
7569     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7570 
7571     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7572     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7573     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7574     if (!cfdof) {
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         indices[ind] = foff + b;
7580       }
7581     } else {
7582       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7583       for (b = 0; b < fdof; ++b) {
7584         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7585         const PetscInt ind    = indperm ? indperm[preind] : preind;
7586 
7587         if ((cind < cfdof) && (b == fcdofs[cind])) {
7588           indices[ind] = -(foff + b + 1);
7589           ++cind;
7590         } else {
7591           indices[ind] = foff + b - cind;
7592         }
7593       }
7594     }
7595     foffs[f] += fdof;
7596   }
7597   PetscFunctionReturn(PETSC_SUCCESS);
7598 }
7599 
7600 static PetscErrorCode DMPlexAnchorsGetSubMatIndices(PetscInt nPoints, const PetscInt pnts[], PetscSection section, PetscSection cSec, PetscInt tmpIndices[], PetscInt fieldOffsets[], PetscInt indices[], const PetscInt ***perms)
7601 {
7602   PetscInt numFields, sStart, sEnd, cStart, cEnd;
7603 
7604   PetscFunctionBegin;
7605   PetscCall(PetscSectionGetNumFields(section, &numFields));
7606   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7607   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7608   for (PetscInt p = 0; p < nPoints; p++) {
7609     PetscInt     b       = pnts[2 * p];
7610     PetscInt     bSecDof = 0, bOff;
7611     PetscInt     cSecDof = 0;
7612     PetscSection indices_section;
7613 
7614     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7615     if (!bSecDof) continue;
7616     if (b >= cStart && b < cEnd) PetscCall(PetscSectionGetDof(cSec, b, &cSecDof));
7617     indices_section = cSecDof > 0 ? cSec : section;
7618     if (numFields) {
7619       PetscInt fStart[32], fEnd[32];
7620 
7621       fStart[0] = 0;
7622       fEnd[0]   = 0;
7623       for (PetscInt f = 0; f < numFields; f++) {
7624         PetscInt fDof = 0;
7625 
7626         PetscCall(PetscSectionGetFieldDof(indices_section, b, f, &fDof));
7627         fStart[f + 1] = fStart[f] + fDof;
7628         fEnd[f + 1]   = fStart[f + 1];
7629       }
7630       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7631       // only apply permutations on one side
7632       PetscCall(DMPlexGetIndicesPointFields_Internal(indices_section, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, perms ? p : -1, NULL, tmpIndices));
7633       for (PetscInt f = 0; f < numFields; f++) {
7634         for (PetscInt i = fStart[f]; i < fEnd[f]; i++) { indices[fieldOffsets[f]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1); }
7635       }
7636     } else {
7637       PetscInt bEnd = 0;
7638 
7639       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7640       PetscCall(DMPlexGetIndicesPoint_Internal(indices_section, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, tmpIndices));
7641 
7642       for (PetscInt i = 0; i < bEnd; i++) indices[fieldOffsets[0]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1);
7643     }
7644   }
7645   PetscFunctionReturn(PETSC_SUCCESS);
7646 }
7647 
7648 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[])
7649 {
7650   Mat             cMat;
7651   PetscSection    aSec, cSec;
7652   IS              aIS;
7653   PetscInt        aStart = -1, aEnd = -1;
7654   PetscInt        sStart = -1, sEnd = -1;
7655   PetscInt        cStart = -1, cEnd = -1;
7656   const PetscInt *anchors;
7657   PetscInt        numFields, p;
7658   PetscInt        newNumPoints = 0, newNumIndices = 0;
7659   PetscInt       *newPoints, *indices, *newIndices, *tmpIndices, *tmpNewIndices;
7660   PetscInt        oldOffsets[32];
7661   PetscInt        newOffsets[32];
7662   PetscInt        oldOffsetsCopy[32];
7663   PetscInt        newOffsetsCopy[32];
7664   PetscScalar    *modMat         = NULL;
7665   PetscBool       anyConstrained = PETSC_FALSE;
7666 
7667   PetscFunctionBegin;
7668   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7669   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7670   PetscCall(PetscSectionGetNumFields(section, &numFields));
7671 
7672   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7673   /* if there are point-to-point constraints */
7674   if (aSec) {
7675     PetscCall(PetscArrayzero(newOffsets, 32));
7676     PetscCall(PetscArrayzero(oldOffsets, 32));
7677     PetscCall(ISGetIndices(aIS, &anchors));
7678     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7679     PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7680     /* figure out how many points are going to be in the new element matrix
7681      * (we allow double counting, because it's all just going to be summed
7682      * into the global matrix anyway) */
7683     for (p = 0; p < 2 * numPoints; p += 2) {
7684       PetscInt b    = points[p];
7685       PetscInt bDof = 0, bSecDof = 0;
7686 
7687       if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7688       if (!bSecDof) continue;
7689 
7690       for (PetscInt f = 0; f < numFields; f++) {
7691         PetscInt fDof = 0;
7692 
7693         PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7694         oldOffsets[f + 1] += fDof;
7695       }
7696       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7697       if (bDof) {
7698         /* this point is constrained */
7699         /* it is going to be replaced by its anchors */
7700         PetscInt bOff, q;
7701 
7702         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7703         for (q = 0; q < bDof; q++) {
7704           PetscInt a    = anchors[bOff + q];
7705           PetscInt aDof = 0;
7706 
7707           if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7708           if (aDof) {
7709             anyConstrained = PETSC_TRUE;
7710             newNumPoints += 1;
7711           }
7712           newNumIndices += aDof;
7713           for (PetscInt f = 0; f < numFields; ++f) {
7714             PetscInt fDof = 0;
7715 
7716             if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7717             newOffsets[f + 1] += fDof;
7718           }
7719         }
7720       } else {
7721         /* this point is not constrained */
7722         newNumPoints++;
7723         newNumIndices += bSecDof;
7724         for (PetscInt f = 0; f < numFields; ++f) {
7725           PetscInt fDof;
7726 
7727           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7728           newOffsets[f + 1] += fDof;
7729         }
7730       }
7731     }
7732   }
7733   if (!anyConstrained) {
7734     if (outNumPoints) *outNumPoints = 0;
7735     if (outNumIndices) *outNumIndices = 0;
7736     if (outPoints) *outPoints = NULL;
7737     if (outMat) *outMat = NULL;
7738     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7739     PetscFunctionReturn(PETSC_SUCCESS);
7740   }
7741 
7742   if (outNumPoints) *outNumPoints = newNumPoints;
7743   if (outNumIndices) *outNumIndices = newNumIndices;
7744 
7745   for (PetscInt f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7746   for (PetscInt f = 0; f < numFields; ++f) oldOffsets[f + 1] += oldOffsets[f];
7747 
7748   if (!outPoints && !outMat) {
7749     if (offsets) {
7750       for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7751     }
7752     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7753     PetscFunctionReturn(PETSC_SUCCESS);
7754   }
7755 
7756   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7757   PetscCheck(!numFields || oldOffsets[numFields] == numIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, oldOffsets[numFields], numIndices);
7758 
7759   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7760   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7761 
7762   /* output arrays */
7763   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7764   PetscCall(PetscArrayzero(newPoints, 2 * newNumPoints));
7765 
7766   // get the new Points
7767   for (PetscInt p = 0, newP = 0; p < numPoints; p++) {
7768     PetscInt b    = points[2 * p];
7769     PetscInt bDof = 0, bSecDof = 0, bOff;
7770 
7771     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7772     if (!bSecDof) continue;
7773     if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7774     if (bDof) {
7775       PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7776       for (PetscInt q = 0; q < bDof; q++) {
7777         PetscInt a = anchors[bOff + q], aDof = 0;
7778 
7779         if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7780         if (aDof) {
7781           newPoints[2 * newP]     = a;
7782           newPoints[2 * newP + 1] = 0; // orientations are accounted for in constructing the matrix, newly added points are in default orientation
7783           newP++;
7784         }
7785       }
7786     } else {
7787       newPoints[2 * newP]     = b;
7788       newPoints[2 * newP + 1] = points[2 * p + 1];
7789       newP++;
7790     }
7791   }
7792 
7793   if (outMat) {
7794     PetscScalar *tmpMat;
7795     PetscCall(PetscArraycpy(oldOffsetsCopy, oldOffsets, 32));
7796     PetscCall(PetscArraycpy(newOffsetsCopy, newOffsets, 32));
7797 
7798     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &indices));
7799     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7800     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7801     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7802 
7803     for (PetscInt i = 0; i < numIndices; i++) indices[i] = -1;
7804     for (PetscInt i = 0; i < newNumIndices; i++) newIndices[i] = -1;
7805 
7806     PetscCall(DMPlexAnchorsGetSubMatIndices(numPoints, points, section, cSec, tmpIndices, oldOffsetsCopy, indices, perms));
7807     PetscCall(DMPlexAnchorsGetSubMatIndices(newNumPoints, newPoints, section, section, tmpNewIndices, newOffsetsCopy, newIndices, NULL));
7808 
7809     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
7810     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7811     PetscCall(PetscArrayzero(modMat, newNumIndices * numIndices));
7812     // for each field, insert the anchor modification into modMat
7813     for (PetscInt f = 0; f < PetscMax(1, numFields); f++) {
7814       PetscInt fStart    = oldOffsets[f];
7815       PetscInt fNewStart = newOffsets[f];
7816       for (PetscInt p = 0, newP = 0, o = fStart, oNew = fNewStart; p < numPoints; p++) {
7817         PetscInt b    = points[2 * p];
7818         PetscInt bDof = 0, bSecDof = 0, bOff;
7819 
7820         if (b >= sStart && b < sEnd) {
7821           if (numFields) {
7822             PetscCall(PetscSectionGetFieldDof(section, b, f, &bSecDof));
7823           } else {
7824             PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7825           }
7826         }
7827         if (!bSecDof) continue;
7828         if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7829         if (bDof) {
7830           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7831           for (PetscInt q = 0; q < bDof; q++, newP++) {
7832             PetscInt a = anchors[bOff + q], aDof = 0;
7833 
7834             if (a >= sStart && a < sEnd) {
7835               if (numFields) {
7836                 PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
7837               } else {
7838                 PetscCall(PetscSectionGetDof(section, a, &aDof));
7839               }
7840             }
7841             if (aDof) {
7842               PetscCall(MatGetValues(cMat, bSecDof, &indices[o], aDof, &newIndices[oNew], tmpMat));
7843               for (PetscInt d = 0; d < bSecDof; d++) {
7844                 for (PetscInt e = 0; e < aDof; e++) modMat[(o + d) * newNumIndices + oNew + e] = tmpMat[d * aDof + e];
7845               }
7846             }
7847             oNew += aDof;
7848           }
7849         } else {
7850           // Insert the identity matrix in this block
7851           for (PetscInt d = 0; d < bSecDof; d++) modMat[(o + d) * newNumIndices + oNew + d] = 1;
7852           oNew += bSecDof;
7853           newP++;
7854         }
7855         o += bSecDof;
7856       }
7857     }
7858 
7859     *outMat = modMat;
7860 
7861     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7862     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7863     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7864     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7865     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &indices));
7866   }
7867   PetscCall(ISRestoreIndices(aIS, &anchors));
7868 
7869   /* output */
7870   if (outPoints) {
7871     *outPoints = newPoints;
7872   } else {
7873     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7874   }
7875   for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7876   PetscFunctionReturn(PETSC_SUCCESS);
7877 }
7878 
7879 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)
7880 {
7881   PetscScalar *modMat        = NULL;
7882   PetscInt     newNumIndices = -1;
7883 
7884   PetscFunctionBegin;
7885   /* 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.
7886      modMat is that matrix C */
7887   PetscCall(DMPlexAnchorsGetSubMatModification(dm, section, numPoints, numIndices, points, perms, outNumPoints, &newNumIndices, outPoints, offsets, outValues ? &modMat : NULL));
7888   if (outNumIndices) *outNumIndices = newNumIndices;
7889   if (modMat) {
7890     const PetscScalar *newValues = values;
7891 
7892     if (multiplyRight) {
7893       PetscScalar *newNewValues = NULL;
7894       PetscBLASInt M, N, K;
7895       PetscScalar  a = 1.0, b = 0.0;
7896 
7897       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);
7898 
7899       PetscCall(PetscBLASIntCast(newNumIndices, &M));
7900       PetscCall(PetscBLASIntCast(numRows, &N));
7901       PetscCall(PetscBLASIntCast(numIndices, &K));
7902       PetscCall(DMGetWorkArray(dm, numRows * newNumIndices, MPIU_SCALAR, &newNewValues));
7903       // row-major to column-major conversion, right multiplication becomes left multiplication
7904       PetscCallBLAS("BLASgemm", BLASgemm_("N", "N", &M, &N, &K, &a, modMat, &M, newValues, &K, &b, newNewValues, &M));
7905       numCols   = newNumIndices;
7906       newValues = newNewValues;
7907     }
7908 
7909     if (multiplyLeft) {
7910       PetscScalar *newNewValues = NULL;
7911       PetscBLASInt M, N, K;
7912       PetscScalar  a = 1.0, b = 0.0;
7913 
7914       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);
7915 
7916       PetscCall(PetscBLASIntCast(numCols, &M));
7917       PetscCall(PetscBLASIntCast(newNumIndices, &N));
7918       PetscCall(PetscBLASIntCast(numIndices, &K));
7919       PetscCall(DMGetWorkArray(dm, newNumIndices * numCols, MPIU_SCALAR, &newNewValues));
7920       // row-major to column-major conversion, left multiplication becomes right multiplication
7921       PetscCallBLAS("BLASgemm", BLASgemm_("N", "T", &M, &N, &K, &a, newValues, &M, modMat, &N, &b, newNewValues, &M));
7922       if (newValues != values) PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &newValues));
7923       newValues = newNewValues;
7924     }
7925     *outValues = (PetscScalar *)newValues;
7926     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
7927   }
7928   PetscFunctionReturn(PETSC_SUCCESS);
7929 }
7930 
7931 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)
7932 {
7933   PetscFunctionBegin;
7934   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, numPoints, numIndices, points, perms, numIndices, numIndices, values, outNumPoints, outNumIndices, outPoints, outValues, offsets, PETSC_TRUE, multiplyLeft));
7935   PetscFunctionReturn(PETSC_SUCCESS);
7936 }
7937 
7938 static PetscErrorCode DMPlexGetClosureIndicesSize_Internal(DM dm, PetscSection section, PetscInt point, PetscInt *closureSize)
7939 {
7940   /* Closure ordering */
7941   PetscSection    clSection;
7942   IS              clPoints;
7943   const PetscInt *clp;
7944   PetscInt       *points;
7945   PetscInt        Ncl, Ni = 0;
7946 
7947   PetscFunctionBeginHot;
7948   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7949   for (PetscInt p = 0; p < Ncl * 2; p += 2) {
7950     PetscInt dof;
7951 
7952     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7953     Ni += dof;
7954   }
7955   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7956   *closureSize = Ni;
7957   PetscFunctionReturn(PETSC_SUCCESS);
7958 }
7959 
7960 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)
7961 {
7962   /* Closure ordering */
7963   PetscSection    clSection;
7964   IS              clPoints;
7965   const PetscInt *clp;
7966   PetscInt       *points;
7967   const PetscInt *clperm = NULL;
7968   /* Dof permutation and sign flips */
7969   const PetscInt    **perms[32] = {NULL};
7970   const PetscScalar **flips[32] = {NULL};
7971   PetscScalar        *valCopy   = NULL;
7972   /* Hanging node constraints */
7973   PetscInt    *pointsC = NULL;
7974   PetscScalar *valuesC = NULL;
7975   PetscInt     NclC, NiC;
7976 
7977   PetscInt *idx;
7978   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7979   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7980   PetscInt  idxStart, idxEnd;
7981   PetscInt  nRows, nCols;
7982 
7983   PetscFunctionBeginHot;
7984   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7985   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7986   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7987   PetscAssertPointer(numRows, 6);
7988   PetscAssertPointer(numCols, 7);
7989   if (indices) PetscAssertPointer(indices, 8);
7990   if (outOffsets) PetscAssertPointer(outOffsets, 9);
7991   if (values) PetscAssertPointer(values, 10);
7992   PetscCall(PetscSectionGetNumFields(section, &Nf));
7993   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7994   PetscCall(PetscArrayzero(offsets, 32));
7995   /* 1) Get points in closure */
7996   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7997   if (useClPerm) {
7998     PetscInt depth, clsize;
7999     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
8000     for (clsize = 0, p = 0; p < Ncl; p++) {
8001       PetscInt dof;
8002       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
8003       clsize += dof;
8004     }
8005     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
8006   }
8007   /* 2) Get number of indices on these points and field offsets from section */
8008   for (p = 0; p < Ncl * 2; p += 2) {
8009     PetscInt dof, fdof;
8010 
8011     PetscCall(PetscSectionGetDof(section, points[p], &dof));
8012     for (f = 0; f < Nf; ++f) {
8013       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
8014       offsets[f + 1] += fdof;
8015     }
8016     Ni += dof;
8017   }
8018   if (*numRows == -1) *numRows = Ni;
8019   if (*numCols == -1) *numCols = Ni;
8020   nRows = *numRows;
8021   nCols = *numCols;
8022   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
8023   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
8024   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
8025   if (multiplyRight) PetscCheck(nCols == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " columns, got %" PetscInt_FMT, Ni, nCols);
8026   if (multiplyLeft) PetscCheck(nRows == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " rows, got %" PetscInt_FMT, Ni, nRows);
8027   for (f = 0; f < PetscMax(1, Nf); ++f) {
8028     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8029     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
8030     /* may need to apply sign changes to the element matrix */
8031     if (values && flips[f]) {
8032       PetscInt foffset = offsets[f];
8033 
8034       for (p = 0; p < Ncl; ++p) {
8035         PetscInt           pnt  = points[2 * p], fdof;
8036         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
8037 
8038         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
8039         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
8040         if (flip) {
8041           PetscInt i, j, k;
8042 
8043           if (!valCopy) {
8044             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8045             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
8046             *values = valCopy;
8047           }
8048           for (i = 0; i < fdof; ++i) {
8049             PetscScalar fval = flip[i];
8050 
8051             if (multiplyRight) {
8052               for (k = 0; k < nRows; ++k) { valCopy[Ni * k + (foffset + i)] *= fval; }
8053             }
8054             if (multiplyLeft) {
8055               for (k = 0; k < nCols; ++k) { valCopy[nCols * (foffset + i) + k] *= fval; }
8056             }
8057           }
8058         }
8059         foffset += fdof;
8060       }
8061     }
8062   }
8063   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
8064   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, Ncl, Ni, points, perms, nRows, nCols, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, multiplyRight, multiplyLeft));
8065   if (NclC) {
8066     if (multiplyRight) *numCols = NiC;
8067     if (multiplyLeft) *numRows = NiC;
8068     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8069     for (f = 0; f < PetscMax(1, Nf); ++f) {
8070       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8071       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8072     }
8073     for (f = 0; f < PetscMax(1, Nf); ++f) {
8074       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
8075       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
8076     }
8077     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8078     Ncl    = NclC;
8079     Ni     = NiC;
8080     points = pointsC;
8081     if (values) *values = valuesC;
8082   }
8083   /* 5) Calculate indices */
8084   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
8085   PetscCall(PetscSectionGetChart(idxSection, &idxStart, &idxEnd));
8086   if (Nf) {
8087     PetscInt  idxOff;
8088     PetscBool useFieldOffsets;
8089 
8090     if (outOffsets) {
8091       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
8092     }
8093     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
8094     if (useFieldOffsets) {
8095       for (p = 0; p < Ncl; ++p) {
8096         const PetscInt pnt = points[p * 2];
8097 
8098         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
8099       }
8100     } else {
8101       for (p = 0; p < Ncl; ++p) {
8102         const PetscInt pnt = points[p * 2];
8103 
8104         if (pnt < idxStart || pnt >= idxEnd) continue;
8105         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8106         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8107          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
8108          * global section. */
8109         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
8110       }
8111     }
8112   } else {
8113     PetscInt off = 0, idxOff;
8114 
8115     for (p = 0; p < Ncl; ++p) {
8116       const PetscInt  pnt  = points[p * 2];
8117       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
8118 
8119       if (pnt < idxStart || pnt >= idxEnd) continue;
8120       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8121       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8122        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
8123       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
8124     }
8125   }
8126   /* 6) Cleanup */
8127   for (f = 0; f < PetscMax(1, Nf); ++f) {
8128     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8129     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8130   }
8131   if (NclC) {
8132     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
8133   } else {
8134     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8135   }
8136 
8137   if (indices) *indices = idx;
8138   PetscFunctionReturn(PETSC_SUCCESS);
8139 }
8140 
8141 /*@C
8142   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
8143 
8144   Not collective
8145 
8146   Input Parameters:
8147 + dm         - The `DM`
8148 . section    - The `PetscSection` describing the points (a local section)
8149 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8150 . point      - The point defining the closure
8151 - useClPerm  - Use the closure point permutation if available
8152 
8153   Output Parameters:
8154 + numIndices - The number of dof indices in the closure of point with the input sections
8155 . indices    - The dof indices
8156 . outOffsets - Array to write the field offsets into, or `NULL`
8157 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8158 
8159   Level: advanced
8160 
8161   Notes:
8162   Call `DMPlexRestoreClosureIndices()` to free allocated memory
8163 
8164   If `idxSection` is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8165   of those indices is not significant.  If `idxSection` is local, the constrained dofs will yield the involution -(idx+1)
8166   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8167   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when `idxSection` == section, otherwise global
8168   indices (with the above semantics) are implied.
8169 
8170 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
8171           `PetscSection`, `DMGetGlobalSection()`
8172 @*/
8173 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
8174 {
8175   PetscInt numRows = -1, numCols = -1;
8176 
8177   PetscFunctionBeginHot;
8178   PetscCall(DMPlexGetClosureIndices_Internal(dm, section, idxSection, point, useClPerm, &numRows, &numCols, indices, outOffsets, values, PETSC_TRUE, PETSC_TRUE));
8179   PetscCheck(numRows == numCols, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Symmetric matrix transformation produces rectangular dimensions (%" PetscInt_FMT ", %" PetscInt_FMT ")", numRows, numCols);
8180   *numIndices = numRows;
8181   PetscFunctionReturn(PETSC_SUCCESS);
8182 }
8183 
8184 /*@C
8185   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
8186 
8187   Not collective
8188 
8189   Input Parameters:
8190 + dm         - The `DM`
8191 . section    - The `PetscSection` describing the points (a local section)
8192 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8193 . point      - The point defining the closure
8194 - useClPerm  - Use the closure point permutation if available
8195 
8196   Output Parameters:
8197 + numIndices - The number of dof indices in the closure of point with the input sections
8198 . indices    - The dof indices
8199 . outOffsets - Array to write the field offsets into, or `NULL`
8200 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8201 
8202   Level: advanced
8203 
8204   Notes:
8205   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
8206 
8207   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8208   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
8209   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8210   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
8211   indices (with the above semantics) are implied.
8212 
8213 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
8214 @*/
8215 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
8216 {
8217   PetscFunctionBegin;
8218   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8219   PetscAssertPointer(indices, 7);
8220   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
8221   PetscFunctionReturn(PETSC_SUCCESS);
8222 }
8223 
8224 PetscErrorCode DMPlexMatSetClosure_Internal(DM dm, PetscSection section, PetscSection globalSection, PetscBool useClPerm, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8225 {
8226   DM_Plex           *mesh = (DM_Plex *)dm->data;
8227   PetscInt          *indices;
8228   PetscInt           numIndices;
8229   const PetscScalar *valuesOrig = values;
8230   PetscErrorCode     ierr;
8231 
8232   PetscFunctionBegin;
8233   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8234   if (!section) PetscCall(DMGetLocalSection(dm, &section));
8235   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
8236   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
8237   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
8238   PetscValidHeaderSpecific(A, MAT_CLASSID, 5);
8239 
8240   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, useClPerm, &numIndices, &indices, NULL, (PetscScalar **)&values));
8241 
8242   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
8243   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8244   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
8245   if (ierr) {
8246     PetscMPIInt rank;
8247 
8248     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8249     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8250     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
8251     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8252     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8253     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
8254   }
8255   if (mesh->printFEM > 1) {
8256     PetscInt i;
8257     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
8258     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
8259     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8260   }
8261 
8262   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8263   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8264   PetscFunctionReturn(PETSC_SUCCESS);
8265 }
8266 
8267 /*@C
8268   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
8269 
8270   Not collective
8271 
8272   Input Parameters:
8273 + dm            - The `DM`
8274 . section       - The section describing the layout in `v`, or `NULL` to use the default section
8275 . globalSection - The section describing the layout in `v`, or `NULL` to use the default global section
8276 . A             - The matrix
8277 . point         - The point in the `DM`
8278 . values        - The array of values
8279 - mode          - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8280 
8281   Level: intermediate
8282 
8283 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8284 @*/
8285 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8286 {
8287   PetscFunctionBegin;
8288   PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, PETSC_TRUE, A, point, values, mode));
8289   PetscFunctionReturn(PETSC_SUCCESS);
8290 }
8291 
8292 /*@C
8293   DMPlexMatSetClosureGeneral - Set an array of the values on the closure of 'point' using a different row and column section
8294 
8295   Not collective
8296 
8297   Input Parameters:
8298 + dmRow            - The `DM` for the row fields
8299 . sectionRow       - The section describing the layout, or `NULL` to use the default section in `dmRow`
8300 . useRowPerm       - The flag to use the closure permutation of the `dmRow` if available
8301 . globalSectionRow - The section describing the layout, or `NULL` to use the default global section in `dmRow`
8302 . dmCol            - The `DM` for the column fields
8303 . sectionCol       - The section describing the layout, or `NULL` to use the default section in `dmCol`
8304 . useColPerm       - The flag to use the closure permutation of the `dmCol` if available
8305 . globalSectionCol - The section describing the layout, or `NULL` to use the default global section in `dmCol`
8306 . A                - The matrix
8307 . point            - The point in the `DM`
8308 . values           - The array of values
8309 - mode             - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8310 
8311   Level: intermediate
8312 
8313 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8314 @*/
8315 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)
8316 {
8317   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
8318   PetscInt          *indicesRow, *indicesCol;
8319   PetscInt           numIndicesRow = -1, numIndicesCol = -1;
8320   const PetscScalar *valuesV0 = values, *valuesV1, *valuesV2;
8321 
8322   PetscErrorCode ierr;
8323 
8324   PetscFunctionBegin;
8325   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
8326   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
8327   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
8328   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
8329   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
8330   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 5);
8331   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
8332   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 6);
8333   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
8334   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 7);
8335   PetscValidHeaderSpecific(A, MAT_CLASSID, 9);
8336 
8337   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmRow, sectionRow, point, &numIndicesRow));
8338   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmCol, sectionCol, point, &numIndicesCol));
8339   valuesV1 = valuesV0;
8340   PetscCall(DMPlexGetClosureIndices_Internal(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV1, PETSC_FALSE, PETSC_TRUE));
8341   valuesV2 = valuesV1;
8342   PetscCall(DMPlexGetClosureIndices_Internal(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesRow, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2, PETSC_TRUE, PETSC_FALSE));
8343 
8344   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2));
8345   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8346   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2, mode);
8347   if (ierr) {
8348     PetscMPIInt rank;
8349 
8350     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8351     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8352     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
8353     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV2));
8354     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8355     if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8356     if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8357   }
8358 
8359   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2));
8360   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8361   if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8362   if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8363   PetscFunctionReturn(PETSC_SUCCESS);
8364 }
8365 
8366 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8367 {
8368   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
8369   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8370   PetscInt       *cpoints = NULL;
8371   PetscInt       *findices, *cindices;
8372   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8373   PetscInt        foffsets[32], coffsets[32];
8374   DMPolytopeType  ct;
8375   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8376   PetscErrorCode  ierr;
8377 
8378   PetscFunctionBegin;
8379   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8380   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8381   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8382   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8383   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8384   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8385   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8386   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8387   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8388   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8389   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
8390   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8391   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8392   PetscCall(PetscArrayzero(foffsets, 32));
8393   PetscCall(PetscArrayzero(coffsets, 32));
8394   /* Column indices */
8395   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8396   maxFPoints = numCPoints;
8397   /* Compress out points not in the section */
8398   /*   TODO: Squeeze out points with 0 dof as well */
8399   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8400   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8401     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8402       cpoints[q * 2]     = cpoints[p];
8403       cpoints[q * 2 + 1] = cpoints[p + 1];
8404       ++q;
8405     }
8406   }
8407   numCPoints = q;
8408   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8409     PetscInt fdof;
8410 
8411     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8412     if (!dof) continue;
8413     for (f = 0; f < numFields; ++f) {
8414       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8415       coffsets[f + 1] += fdof;
8416     }
8417     numCIndices += dof;
8418   }
8419   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8420   /* Row indices */
8421   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8422   {
8423     DMPlexTransform tr;
8424     DMPolytopeType *rct;
8425     PetscInt       *rsize, *rcone, *rornt, Nt;
8426 
8427     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8428     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8429     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8430     numSubcells = rsize[Nt - 1];
8431     PetscCall(DMPlexTransformDestroy(&tr));
8432   }
8433   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8434   for (r = 0, q = 0; r < numSubcells; ++r) {
8435     /* TODO Map from coarse to fine cells */
8436     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8437     /* Compress out points not in the section */
8438     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8439     for (p = 0; p < numFPoints * 2; p += 2) {
8440       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8441         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8442         if (!dof) continue;
8443         for (s = 0; s < q; ++s)
8444           if (fpoints[p] == ftotpoints[s * 2]) break;
8445         if (s < q) continue;
8446         ftotpoints[q * 2]     = fpoints[p];
8447         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8448         ++q;
8449       }
8450     }
8451     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8452   }
8453   numFPoints = q;
8454   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8455     PetscInt fdof;
8456 
8457     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8458     if (!dof) continue;
8459     for (f = 0; f < numFields; ++f) {
8460       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8461       foffsets[f + 1] += fdof;
8462     }
8463     numFIndices += dof;
8464   }
8465   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8466 
8467   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8468   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8469   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8470   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8471   if (numFields) {
8472     const PetscInt **permsF[32] = {NULL};
8473     const PetscInt **permsC[32] = {NULL};
8474 
8475     for (f = 0; f < numFields; f++) {
8476       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8477       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8478     }
8479     for (p = 0; p < numFPoints; p++) {
8480       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8481       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8482     }
8483     for (p = 0; p < numCPoints; p++) {
8484       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8485       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8486     }
8487     for (f = 0; f < numFields; f++) {
8488       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8489       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8490     }
8491   } else {
8492     const PetscInt **permsF = NULL;
8493     const PetscInt **permsC = NULL;
8494 
8495     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8496     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8497     for (p = 0, off = 0; p < numFPoints; p++) {
8498       const PetscInt *perm = permsF ? permsF[p] : NULL;
8499 
8500       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8501       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8502     }
8503     for (p = 0, off = 0; p < numCPoints; p++) {
8504       const PetscInt *perm = permsC ? permsC[p] : NULL;
8505 
8506       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8507       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8508     }
8509     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8510     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8511   }
8512   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8513   /* TODO: flips */
8514   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8515   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8516   if (ierr) {
8517     PetscMPIInt rank;
8518 
8519     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8520     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8521     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8522     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8523     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8524   }
8525   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8526   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8527   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8528   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8529   PetscFunctionReturn(PETSC_SUCCESS);
8530 }
8531 
8532 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8533 {
8534   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8535   PetscInt       *cpoints      = NULL;
8536   PetscInt        foffsets[32] = {0}, coffsets[32] = {0};
8537   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8538   DMPolytopeType  ct;
8539   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8540 
8541   PetscFunctionBegin;
8542   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8543   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8544   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8545   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8546   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8547   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8548   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8549   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8550   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8551   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8552   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8553   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8554   /* Column indices */
8555   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8556   maxFPoints = numCPoints;
8557   /* Compress out points not in the section */
8558   /*   TODO: Squeeze out points with 0 dof as well */
8559   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8560   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8561     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8562       cpoints[q * 2]     = cpoints[p];
8563       cpoints[q * 2 + 1] = cpoints[p + 1];
8564       ++q;
8565     }
8566   }
8567   numCPoints = q;
8568   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8569     PetscInt fdof;
8570 
8571     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8572     if (!dof) continue;
8573     for (f = 0; f < numFields; ++f) {
8574       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8575       coffsets[f + 1] += fdof;
8576     }
8577     numCIndices += dof;
8578   }
8579   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8580   /* Row indices */
8581   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8582   {
8583     DMPlexTransform tr;
8584     DMPolytopeType *rct;
8585     PetscInt       *rsize, *rcone, *rornt, Nt;
8586 
8587     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8588     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8589     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8590     numSubcells = rsize[Nt - 1];
8591     PetscCall(DMPlexTransformDestroy(&tr));
8592   }
8593   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8594   for (r = 0, q = 0; r < numSubcells; ++r) {
8595     /* TODO Map from coarse to fine cells */
8596     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8597     /* Compress out points not in the section */
8598     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8599     for (p = 0; p < numFPoints * 2; p += 2) {
8600       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8601         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8602         if (!dof) continue;
8603         for (s = 0; s < q; ++s)
8604           if (fpoints[p] == ftotpoints[s * 2]) break;
8605         if (s < q) continue;
8606         ftotpoints[q * 2]     = fpoints[p];
8607         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8608         ++q;
8609       }
8610     }
8611     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8612   }
8613   numFPoints = q;
8614   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8615     PetscInt fdof;
8616 
8617     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8618     if (!dof) continue;
8619     for (f = 0; f < numFields; ++f) {
8620       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8621       foffsets[f + 1] += fdof;
8622     }
8623     numFIndices += dof;
8624   }
8625   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8626 
8627   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8628   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8629   if (numFields) {
8630     const PetscInt **permsF[32] = {NULL};
8631     const PetscInt **permsC[32] = {NULL};
8632 
8633     for (f = 0; f < numFields; f++) {
8634       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8635       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8636     }
8637     for (p = 0; p < numFPoints; p++) {
8638       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8639       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8640     }
8641     for (p = 0; p < numCPoints; p++) {
8642       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8643       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8644     }
8645     for (f = 0; f < numFields; f++) {
8646       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8647       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8648     }
8649   } else {
8650     const PetscInt **permsF = NULL;
8651     const PetscInt **permsC = NULL;
8652 
8653     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8654     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8655     for (p = 0, off = 0; p < numFPoints; p++) {
8656       const PetscInt *perm = permsF ? permsF[p] : NULL;
8657 
8658       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8659       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8660     }
8661     for (p = 0, off = 0; p < numCPoints; p++) {
8662       const PetscInt *perm = permsC ? permsC[p] : NULL;
8663 
8664       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8665       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8666     }
8667     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8668     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8669   }
8670   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8671   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8672   PetscFunctionReturn(PETSC_SUCCESS);
8673 }
8674 
8675 /*@
8676   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8677 
8678   Input Parameter:
8679 . dm - The `DMPLEX` object
8680 
8681   Output Parameter:
8682 . cellHeight - The height of a cell
8683 
8684   Level: developer
8685 
8686 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetVTKCellHeight()`
8687 @*/
8688 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8689 {
8690   DM_Plex *mesh = (DM_Plex *)dm->data;
8691 
8692   PetscFunctionBegin;
8693   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8694   PetscAssertPointer(cellHeight, 2);
8695   *cellHeight = mesh->vtkCellHeight;
8696   PetscFunctionReturn(PETSC_SUCCESS);
8697 }
8698 
8699 /*@
8700   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8701 
8702   Input Parameters:
8703 + dm         - The `DMPLEX` object
8704 - cellHeight - The height of a cell
8705 
8706   Level: developer
8707 
8708 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8709 @*/
8710 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8711 {
8712   DM_Plex *mesh = (DM_Plex *)dm->data;
8713 
8714   PetscFunctionBegin;
8715   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8716   mesh->vtkCellHeight = cellHeight;
8717   PetscFunctionReturn(PETSC_SUCCESS);
8718 }
8719 
8720 /*@
8721   DMPlexGetCellTypeStratum - Get the range of cells of a given celltype
8722 
8723   Input Parameters:
8724 + dm - The `DMPLEX` object
8725 - ct - The `DMPolytopeType` of the cell
8726 
8727   Output Parameters:
8728 + start - The first cell of this type, or `NULL`
8729 - end   - The upper bound on this celltype, or `NULL`
8730 
8731   Level: advanced
8732 
8733 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8734 @*/
8735 PetscErrorCode DMPlexGetCellTypeStratum(DM dm, DMPolytopeType ct, PetscInt *start, PetscInt *end)
8736 {
8737   DM_Plex *mesh = (DM_Plex *)dm->data;
8738   DMLabel  label;
8739   PetscInt pStart, pEnd;
8740 
8741   PetscFunctionBegin;
8742   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8743   if (start) {
8744     PetscAssertPointer(start, 3);
8745     *start = 0;
8746   }
8747   if (end) {
8748     PetscAssertPointer(end, 4);
8749     *end = 0;
8750   }
8751   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8752   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
8753   if (mesh->tr) {
8754     PetscCall(DMPlexTransformGetCellTypeStratum(mesh->tr, ct, start, end));
8755   } else {
8756     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
8757     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named celltype was found");
8758     PetscCall(DMLabelGetStratumBounds(label, ct, start, end));
8759   }
8760   PetscFunctionReturn(PETSC_SUCCESS);
8761 }
8762 
8763 /*@
8764   DMPlexGetDepthStratumGlobalSize - Get the global size for a given depth stratum
8765 
8766   Input Parameters:
8767 + dm    - The `DMPLEX` object
8768 - depth - The depth for the given point stratum
8769 
8770   Output Parameter:
8771 . gsize - The global number of points in the stratum
8772 
8773   Level: advanced
8774 
8775 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8776 @*/
8777 PetscErrorCode DMPlexGetDepthStratumGlobalSize(DM dm, PetscInt depth, PetscInt *gsize)
8778 {
8779   PetscSF         sf;
8780   const PetscInt *leaves;
8781   PetscInt        Nl, loc, start, end, lsize = 0;
8782 
8783   PetscFunctionBegin;
8784   PetscCall(DMGetPointSF(dm, &sf));
8785   PetscCall(PetscSFGetGraph(sf, NULL, &Nl, &leaves, NULL));
8786   PetscCall(DMPlexGetDepthStratum(dm, depth, &start, &end));
8787   for (PetscInt p = start; p < end; ++p) {
8788     PetscCall(PetscFindInt(p, Nl, leaves, &loc));
8789     if (loc < 0) ++lsize;
8790   }
8791   PetscCallMPI(MPI_Allreduce(&lsize, gsize, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
8792   PetscFunctionReturn(PETSC_SUCCESS);
8793 }
8794 
8795 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8796 {
8797   PetscSection section, globalSection;
8798   PetscInt    *numbers, p;
8799 
8800   PetscFunctionBegin;
8801   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8802   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8803   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8804   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8805   PetscCall(PetscSectionSetUp(section));
8806   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &globalSection));
8807   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8808   for (p = pStart; p < pEnd; ++p) {
8809     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8810     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8811     else numbers[p - pStart] += shift;
8812   }
8813   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8814   if (globalSize) {
8815     PetscLayout layout;
8816     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8817     PetscCall(PetscLayoutGetSize(layout, globalSize));
8818     PetscCall(PetscLayoutDestroy(&layout));
8819   }
8820   PetscCall(PetscSectionDestroy(&section));
8821   PetscCall(PetscSectionDestroy(&globalSection));
8822   PetscFunctionReturn(PETSC_SUCCESS);
8823 }
8824 
8825 /*@
8826   DMPlexCreateCellNumbering - Get a global cell numbering for all cells on this process
8827 
8828   Input Parameters:
8829 + dm         - The `DMPLEX` object
8830 - includeAll - Whether to include all cells, or just the simplex and box cells
8831 
8832   Output Parameter:
8833 . globalCellNumbers - Global cell numbers for all cells on this process
8834 
8835   Level: developer
8836 
8837 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`
8838 @*/
8839 PetscErrorCode DMPlexCreateCellNumbering(DM dm, PetscBool includeAll, IS *globalCellNumbers)
8840 {
8841   PetscInt cellHeight, cStart, cEnd;
8842 
8843   PetscFunctionBegin;
8844   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8845   if (includeAll) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8846   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8847   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8848   PetscFunctionReturn(PETSC_SUCCESS);
8849 }
8850 
8851 /*@
8852   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8853 
8854   Input Parameter:
8855 . dm - The `DMPLEX` object
8856 
8857   Output Parameter:
8858 . globalCellNumbers - Global cell numbers for all cells on this process
8859 
8860   Level: developer
8861 
8862 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateCellNumbering()`, `DMPlexGetVertexNumbering()`
8863 @*/
8864 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8865 {
8866   DM_Plex *mesh = (DM_Plex *)dm->data;
8867 
8868   PetscFunctionBegin;
8869   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8870   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8871   *globalCellNumbers = mesh->globalCellNumbers;
8872   PetscFunctionReturn(PETSC_SUCCESS);
8873 }
8874 
8875 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8876 {
8877   PetscInt vStart, vEnd;
8878 
8879   PetscFunctionBegin;
8880   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8881   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8882   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8883   PetscFunctionReturn(PETSC_SUCCESS);
8884 }
8885 
8886 /*@
8887   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8888 
8889   Input Parameter:
8890 . dm - The `DMPLEX` object
8891 
8892   Output Parameter:
8893 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8894 
8895   Level: developer
8896 
8897 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8898 @*/
8899 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8900 {
8901   DM_Plex *mesh = (DM_Plex *)dm->data;
8902 
8903   PetscFunctionBegin;
8904   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8905   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8906   *globalVertexNumbers = mesh->globalVertexNumbers;
8907   PetscFunctionReturn(PETSC_SUCCESS);
8908 }
8909 
8910 /*@
8911   DMPlexCreatePointNumbering - Create a global numbering for all points.
8912 
8913   Collective
8914 
8915   Input Parameter:
8916 . dm - The `DMPLEX` object
8917 
8918   Output Parameter:
8919 . globalPointNumbers - Global numbers for all points on this process
8920 
8921   Level: developer
8922 
8923   Notes:
8924   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8925   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8926   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8927   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8928 
8929   The partitioned mesh is
8930   ```
8931   (2)--0--(3)--1--(4)    (1)--0--(2)
8932   ```
8933   and its global numbering is
8934   ```
8935   (3)--0--(4)--1--(5)--2--(6)
8936   ```
8937   Then the global numbering is provided as
8938   ```
8939   [0] Number of indices in set 5
8940   [0] 0 0
8941   [0] 1 1
8942   [0] 2 3
8943   [0] 3 4
8944   [0] 4 -6
8945   [1] Number of indices in set 3
8946   [1] 0 2
8947   [1] 1 5
8948   [1] 2 6
8949   ```
8950 
8951 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8952 @*/
8953 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8954 {
8955   IS        nums[4];
8956   PetscInt  depths[4], gdepths[4], starts[4];
8957   PetscInt  depth, d, shift = 0;
8958   PetscBool empty = PETSC_FALSE;
8959 
8960   PetscFunctionBegin;
8961   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8962   PetscCall(DMPlexGetDepth(dm, &depth));
8963   // For unstratified meshes use dim instead of depth
8964   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8965   // If any stratum is empty, we must mark all empty
8966   for (d = 0; d <= depth; ++d) {
8967     PetscInt end;
8968 
8969     depths[d] = depth - d;
8970     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8971     if (!(starts[d] - end)) empty = PETSC_TRUE;
8972   }
8973   if (empty)
8974     for (d = 0; d <= depth; ++d) {
8975       depths[d] = -1;
8976       starts[d] = -1;
8977     }
8978   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8979   PetscCallMPI(MPIU_Allreduce(depths, gdepths, (PetscMPIInt)(depth + 1), MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8980   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]);
8981   // Note here that 'shift' is collective, so that the numbering is stratified by depth
8982   for (d = 0; d <= depth; ++d) {
8983     PetscInt pStart, pEnd, gsize;
8984 
8985     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8986     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8987     shift += gsize;
8988   }
8989   PetscCall(ISConcatenate(PETSC_COMM_SELF, depth + 1, nums, globalPointNumbers));
8990   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8991   PetscFunctionReturn(PETSC_SUCCESS);
8992 }
8993 
8994 /*@
8995   DMPlexCreateEdgeNumbering - Create a global numbering for edges.
8996 
8997   Collective
8998 
8999   Input Parameter:
9000 . dm - The `DMPLEX` object
9001 
9002   Output Parameter:
9003 . globalEdgeNumbers - Global numbers for all edges on this process
9004 
9005   Level: developer
9006 
9007   Notes:
9008   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).
9009 
9010 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`, `DMPlexCreatePointNumbering()`
9011 @*/
9012 PetscErrorCode DMPlexCreateEdgeNumbering(DM dm, IS *globalEdgeNumbers)
9013 {
9014   PetscSF  sf;
9015   PetscInt eStart, eEnd;
9016 
9017   PetscFunctionBegin;
9018   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9019   PetscCall(DMGetPointSF(dm, &sf));
9020   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9021   PetscCall(DMPlexCreateNumbering_Plex(dm, eStart, eEnd, 0, NULL, sf, globalEdgeNumbers));
9022   PetscFunctionReturn(PETSC_SUCCESS);
9023 }
9024 
9025 /*@
9026   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
9027 
9028   Input Parameter:
9029 . dm - The `DMPLEX` object
9030 
9031   Output Parameter:
9032 . ranks - The rank field
9033 
9034   Options Database Key:
9035 . -dm_partition_view - Adds the rank field into the `DM` output from `-dm_view` using the same viewer
9036 
9037   Level: intermediate
9038 
9039 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
9040 @*/
9041 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
9042 {
9043   DM             rdm;
9044   PetscFE        fe;
9045   PetscScalar   *r;
9046   PetscMPIInt    rank;
9047   DMPolytopeType ct;
9048   PetscInt       dim, cStart, cEnd, c;
9049   PetscBool      simplex;
9050 
9051   PetscFunctionBeginUser;
9052   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9053   PetscAssertPointer(ranks, 2);
9054   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
9055   PetscCall(DMClone(dm, &rdm));
9056   PetscCall(DMGetDimension(rdm, &dim));
9057   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
9058   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
9059   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
9060   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
9061   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
9062   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
9063   PetscCall(PetscFEDestroy(&fe));
9064   PetscCall(DMCreateDS(rdm));
9065   PetscCall(DMCreateGlobalVector(rdm, ranks));
9066   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
9067   PetscCall(VecGetArray(*ranks, &r));
9068   for (c = cStart; c < cEnd; ++c) {
9069     PetscScalar *lr;
9070 
9071     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
9072     if (lr) *lr = rank;
9073   }
9074   PetscCall(VecRestoreArray(*ranks, &r));
9075   PetscCall(DMDestroy(&rdm));
9076   PetscFunctionReturn(PETSC_SUCCESS);
9077 }
9078 
9079 /*@
9080   DMPlexCreateLabelField - Create a field whose value is the label value for that point
9081 
9082   Input Parameters:
9083 + dm    - The `DMPLEX`
9084 - label - The `DMLabel`
9085 
9086   Output Parameter:
9087 . val - The label value field
9088 
9089   Options Database Key:
9090 . -dm_label_view - Adds the label value field into the `DM` output from `-dm_view` using the same viewer
9091 
9092   Level: intermediate
9093 
9094 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
9095 @*/
9096 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
9097 {
9098   DM             rdm, plex;
9099   Vec            lval;
9100   PetscSection   section;
9101   PetscFE        fe;
9102   PetscScalar   *v;
9103   PetscInt       dim, pStart, pEnd, p, cStart;
9104   DMPolytopeType ct;
9105   char           name[PETSC_MAX_PATH_LEN];
9106   const char    *lname, *prefix;
9107 
9108   PetscFunctionBeginUser;
9109   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9110   PetscAssertPointer(label, 2);
9111   PetscAssertPointer(val, 3);
9112   PetscCall(DMClone(dm, &rdm));
9113   PetscCall(DMConvert(rdm, DMPLEX, &plex));
9114   PetscCall(DMPlexGetHeightStratum(plex, 0, &cStart, NULL));
9115   PetscCall(DMPlexGetCellType(plex, cStart, &ct));
9116   PetscCall(DMDestroy(&plex));
9117   PetscCall(DMGetDimension(rdm, &dim));
9118   PetscCall(DMGetOptionsPrefix(dm, &prefix));
9119   PetscCall(PetscObjectGetName((PetscObject)label, &lname));
9120   PetscCall(PetscSNPrintf(name, sizeof(name), "%s%s_", prefix ? prefix : "", lname));
9121   PetscCall(PetscFECreateByCell(PETSC_COMM_SELF, dim, 1, ct, name, -1, &fe));
9122   PetscCall(PetscObjectSetName((PetscObject)fe, ""));
9123   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
9124   PetscCall(PetscFEDestroy(&fe));
9125   PetscCall(DMCreateDS(rdm));
9126   PetscCall(DMCreateGlobalVector(rdm, val));
9127   PetscCall(DMCreateLocalVector(rdm, &lval));
9128   PetscCall(PetscObjectSetName((PetscObject)*val, lname));
9129   PetscCall(DMGetLocalSection(rdm, &section));
9130   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
9131   PetscCall(VecGetArray(lval, &v));
9132   for (p = pStart; p < pEnd; ++p) {
9133     PetscInt cval, dof, off;
9134 
9135     PetscCall(PetscSectionGetDof(section, p, &dof));
9136     if (!dof) continue;
9137     PetscCall(DMLabelGetValue(label, p, &cval));
9138     PetscCall(PetscSectionGetOffset(section, p, &off));
9139     for (PetscInt d = 0; d < dof; d++) v[off + d] = cval;
9140   }
9141   PetscCall(VecRestoreArray(lval, &v));
9142   PetscCall(DMLocalToGlobal(rdm, lval, INSERT_VALUES, *val));
9143   PetscCall(VecDestroy(&lval));
9144   PetscCall(DMDestroy(&rdm));
9145   PetscFunctionReturn(PETSC_SUCCESS);
9146 }
9147 
9148 /*@
9149   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
9150 
9151   Input Parameter:
9152 . dm - The `DMPLEX` object
9153 
9154   Level: developer
9155 
9156   Notes:
9157   This is a useful diagnostic when creating meshes programmatically.
9158 
9159   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9160 
9161 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9162 @*/
9163 PetscErrorCode DMPlexCheckSymmetry(DM dm)
9164 {
9165   PetscSection    coneSection, supportSection;
9166   const PetscInt *cone, *support;
9167   PetscInt        coneSize, c, supportSize, s;
9168   PetscInt        pStart, pEnd, p, pp, csize, ssize;
9169   PetscBool       storagecheck = PETSC_TRUE;
9170 
9171   PetscFunctionBegin;
9172   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9173   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
9174   PetscCall(DMPlexGetConeSection(dm, &coneSection));
9175   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
9176   /* Check that point p is found in the support of its cone points, and vice versa */
9177   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9178   for (p = pStart; p < pEnd; ++p) {
9179     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
9180     PetscCall(DMPlexGetCone(dm, p, &cone));
9181     for (c = 0; c < coneSize; ++c) {
9182       PetscBool dup = PETSC_FALSE;
9183       PetscInt  d;
9184       for (d = c - 1; d >= 0; --d) {
9185         if (cone[c] == cone[d]) {
9186           dup = PETSC_TRUE;
9187           break;
9188         }
9189       }
9190       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
9191       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
9192       for (s = 0; s < supportSize; ++s) {
9193         if (support[s] == p) break;
9194       }
9195       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
9196         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
9197         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
9198         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9199         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
9200         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
9201         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9202         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]);
9203         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
9204       }
9205     }
9206     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
9207     if (p != pp) {
9208       storagecheck = PETSC_FALSE;
9209       continue;
9210     }
9211     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
9212     PetscCall(DMPlexGetSupport(dm, p, &support));
9213     for (s = 0; s < supportSize; ++s) {
9214       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
9215       PetscCall(DMPlexGetCone(dm, support[s], &cone));
9216       for (c = 0; c < coneSize; ++c) {
9217         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
9218         if (cone[c] != pp) {
9219           c = 0;
9220           break;
9221         }
9222         if (cone[c] == p) break;
9223       }
9224       if (c >= coneSize) {
9225         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
9226         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
9227         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9228         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
9229         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
9230         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9231         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
9232       }
9233     }
9234   }
9235   if (storagecheck) {
9236     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
9237     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
9238     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
9239   }
9240   PetscFunctionReturn(PETSC_SUCCESS);
9241 }
9242 
9243 /*
9244   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.
9245 */
9246 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
9247 {
9248   DMPolytopeType  cct;
9249   PetscInt        ptpoints[4];
9250   const PetscInt *cone, *ccone, *ptcone;
9251   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
9252 
9253   PetscFunctionBegin;
9254   *unsplit = 0;
9255   switch (ct) {
9256   case DM_POLYTOPE_POINT_PRISM_TENSOR:
9257     ptpoints[npt++] = c;
9258     break;
9259   case DM_POLYTOPE_SEG_PRISM_TENSOR:
9260     PetscCall(DMPlexGetCone(dm, c, &cone));
9261     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9262     for (cp = 0; cp < coneSize; ++cp) {
9263       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
9264       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
9265     }
9266     break;
9267   case DM_POLYTOPE_TRI_PRISM_TENSOR:
9268   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9269     PetscCall(DMPlexGetCone(dm, c, &cone));
9270     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9271     for (cp = 0; cp < coneSize; ++cp) {
9272       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
9273       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
9274       for (ccp = 0; ccp < cconeSize; ++ccp) {
9275         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
9276         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
9277           PetscInt p;
9278           for (p = 0; p < npt; ++p)
9279             if (ptpoints[p] == ccone[ccp]) break;
9280           if (p == npt) ptpoints[npt++] = ccone[ccp];
9281         }
9282       }
9283     }
9284     break;
9285   default:
9286     break;
9287   }
9288   for (pt = 0; pt < npt; ++pt) {
9289     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
9290     if (ptcone[0] == ptcone[1]) ++(*unsplit);
9291   }
9292   PetscFunctionReturn(PETSC_SUCCESS);
9293 }
9294 
9295 /*@
9296   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
9297 
9298   Input Parameters:
9299 + dm         - The `DMPLEX` object
9300 - cellHeight - Normally 0
9301 
9302   Level: developer
9303 
9304   Notes:
9305   This is a useful diagnostic when creating meshes programmatically.
9306   Currently applicable only to homogeneous simplex or tensor meshes.
9307 
9308   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9309 
9310 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9311 @*/
9312 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
9313 {
9314   DMPlexInterpolatedFlag interp;
9315   DMPolytopeType         ct;
9316   PetscInt               vStart, vEnd, cStart, cEnd, c;
9317 
9318   PetscFunctionBegin;
9319   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9320   PetscCall(DMPlexIsInterpolated(dm, &interp));
9321   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9322   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9323   for (c = cStart; c < cEnd; ++c) {
9324     PetscInt *closure = NULL;
9325     PetscInt  coneSize, closureSize, cl, Nv = 0;
9326 
9327     PetscCall(DMPlexGetCellType(dm, c, &ct));
9328     if (ct == DM_POLYTOPE_UNKNOWN) continue;
9329     if (interp == DMPLEX_INTERPOLATED_FULL) {
9330       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9331       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));
9332     }
9333     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9334     for (cl = 0; cl < closureSize * 2; cl += 2) {
9335       const PetscInt p = closure[cl];
9336       if ((p >= vStart) && (p < vEnd)) ++Nv;
9337     }
9338     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9339     /* Special Case: Tensor faces with identified vertices */
9340     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
9341       PetscInt unsplit;
9342 
9343       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9344       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
9345     }
9346     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));
9347   }
9348   PetscFunctionReturn(PETSC_SUCCESS);
9349 }
9350 
9351 /*@
9352   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
9353 
9354   Collective
9355 
9356   Input Parameters:
9357 + dm         - The `DMPLEX` object
9358 - cellHeight - Normally 0
9359 
9360   Level: developer
9361 
9362   Notes:
9363   This is a useful diagnostic when creating meshes programmatically.
9364   This routine is only relevant for meshes that are fully interpolated across all ranks.
9365   It will error out if a partially interpolated mesh is given on some rank.
9366   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
9367 
9368   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9369 
9370 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
9371 @*/
9372 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
9373 {
9374   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
9375   DMPlexInterpolatedFlag interpEnum;
9376 
9377   PetscFunctionBegin;
9378   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9379   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
9380   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(PETSC_SUCCESS);
9381   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
9382     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported"));
9383     PetscFunctionReturn(PETSC_SUCCESS);
9384   }
9385 
9386   PetscCall(DMGetDimension(dm, &dim));
9387   PetscCall(DMPlexGetDepth(dm, &depth));
9388   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9389   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
9390     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
9391     for (c = cStart; c < cEnd; ++c) {
9392       const PetscInt       *cone, *ornt, *faceSizes, *faces;
9393       const DMPolytopeType *faceTypes;
9394       DMPolytopeType        ct;
9395       PetscInt              numFaces, coneSize, f;
9396       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
9397 
9398       PetscCall(DMPlexGetCellType(dm, c, &ct));
9399       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9400       if (unsplit) continue;
9401       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9402       PetscCall(DMPlexGetCone(dm, c, &cone));
9403       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
9404       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9405       for (cl = 0; cl < closureSize * 2; cl += 2) {
9406         const PetscInt p = closure[cl];
9407         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
9408       }
9409       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9410       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);
9411       for (f = 0; f < numFaces; ++f) {
9412         DMPolytopeType fct;
9413         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
9414 
9415         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
9416         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
9417         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
9418           const PetscInt p = fclosure[cl];
9419           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
9420         }
9421         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]);
9422         for (v = 0; v < fnumCorners; ++v) {
9423           if (fclosure[v] != faces[fOff + v]) {
9424             PetscInt v1;
9425 
9426             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
9427             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
9428             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
9429             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
9430             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9431             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]);
9432           }
9433         }
9434         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
9435         fOff += faceSizes[f];
9436       }
9437       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9438       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9439     }
9440   }
9441   PetscFunctionReturn(PETSC_SUCCESS);
9442 }
9443 
9444 /*@
9445   DMPlexCheckGeometry - Check the geometry of mesh cells
9446 
9447   Input Parameter:
9448 . dm - The `DMPLEX` object
9449 
9450   Level: developer
9451 
9452   Notes:
9453   This is a useful diagnostic when creating meshes programmatically.
9454 
9455   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9456 
9457 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9458 @*/
9459 PetscErrorCode DMPlexCheckGeometry(DM dm)
9460 {
9461   Vec       coordinates;
9462   PetscReal detJ, J[9], refVol = 1.0;
9463   PetscReal vol;
9464   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
9465 
9466   PetscFunctionBegin;
9467   PetscCall(DMGetDimension(dm, &dim));
9468   PetscCall(DMGetCoordinateDim(dm, &dE));
9469   if (dim != dE) PetscFunctionReturn(PETSC_SUCCESS);
9470   PetscCall(DMPlexGetDepth(dm, &depth));
9471   for (d = 0; d < dim; ++d) refVol *= 2.0;
9472   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9473   /* Make sure local coordinates are created, because that step is collective */
9474   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
9475   if (!coordinates) PetscFunctionReturn(PETSC_SUCCESS);
9476   for (c = cStart; c < cEnd; ++c) {
9477     DMPolytopeType ct;
9478     PetscInt       unsplit;
9479     PetscBool      ignoreZeroVol = PETSC_FALSE;
9480 
9481     PetscCall(DMPlexGetCellType(dm, c, &ct));
9482     switch (ct) {
9483     case DM_POLYTOPE_SEG_PRISM_TENSOR:
9484     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9485     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9486       ignoreZeroVol = PETSC_TRUE;
9487       break;
9488     default:
9489       break;
9490     }
9491     switch (ct) {
9492     case DM_POLYTOPE_TRI_PRISM:
9493     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9494     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9495     case DM_POLYTOPE_PYRAMID:
9496       continue;
9497     default:
9498       break;
9499     }
9500     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9501     if (unsplit) continue;
9502     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
9503     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);
9504     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
9505     /* This should work with periodicity since DG coordinates should be used */
9506     if (depth > 1) {
9507       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
9508       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);
9509       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
9510     }
9511   }
9512   PetscFunctionReturn(PETSC_SUCCESS);
9513 }
9514 
9515 /*@
9516   DMPlexCheckPointSF - Check that several necessary conditions are met for the point `PetscSF` of this plex.
9517 
9518   Collective
9519 
9520   Input Parameters:
9521 + dm              - The `DMPLEX` object
9522 . pointSF         - The `PetscSF`, or `NULL` for `PointSF` attached to `DM`
9523 - allowExtraRoots - Flag to allow extra points not present in the `DM`
9524 
9525   Level: developer
9526 
9527   Notes:
9528   This is mainly intended for debugging/testing purposes.
9529 
9530   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9531 
9532   Extra roots can come from periodic cuts, where additional points appear on the boundary
9533 
9534 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
9535 @*/
9536 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
9537 {
9538   PetscInt           l, nleaves, nroots, overlap;
9539   const PetscInt    *locals;
9540   const PetscSFNode *remotes;
9541   PetscBool          distributed;
9542   MPI_Comm           comm;
9543   PetscMPIInt        rank;
9544 
9545   PetscFunctionBegin;
9546   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9547   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
9548   else pointSF = dm->sf;
9549   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9550   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
9551   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9552   {
9553     PetscMPIInt mpiFlag;
9554 
9555     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
9556     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
9557   }
9558   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
9559   PetscCall(DMPlexIsDistributed(dm, &distributed));
9560   if (!distributed) {
9561     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);
9562     PetscFunctionReturn(PETSC_SUCCESS);
9563   }
9564   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);
9565   PetscCall(DMPlexGetOverlap(dm, &overlap));
9566 
9567   /* Check SF graph is compatible with DMPlex chart */
9568   {
9569     PetscInt pStart, pEnd, maxLeaf;
9570 
9571     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9572     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9573     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9574     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9575   }
9576 
9577   /* Check Point SF has no local points referenced */
9578   for (l = 0; l < nleaves; l++) {
9579     PetscAssert(remotes[l].rank != (PetscInt)rank, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains local point %" PetscInt_FMT " <- (%d,%" PetscInt_FMT ")", locals ? locals[l] : l, (PetscMPIInt)remotes[l].rank, remotes[l].index);
9580   }
9581 
9582   /* Check there are no cells in interface */
9583   if (!overlap) {
9584     PetscInt cellHeight, cStart, cEnd;
9585 
9586     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9587     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9588     for (l = 0; l < nleaves; ++l) {
9589       const PetscInt point = locals ? locals[l] : l;
9590 
9591       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9592     }
9593   }
9594 
9595   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9596   {
9597     const PetscInt *rootdegree;
9598 
9599     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9600     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9601     for (l = 0; l < nleaves; ++l) {
9602       const PetscInt  point = locals ? locals[l] : l;
9603       const PetscInt *cone;
9604       PetscInt        coneSize, c, idx;
9605 
9606       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9607       PetscCall(DMPlexGetCone(dm, point, &cone));
9608       for (c = 0; c < coneSize; ++c) {
9609         if (!rootdegree[cone[c]]) {
9610           if (locals) {
9611             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9612           } else {
9613             idx = (cone[c] < nleaves) ? cone[c] : -1;
9614           }
9615           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9616         }
9617       }
9618     }
9619   }
9620   PetscFunctionReturn(PETSC_SUCCESS);
9621 }
9622 
9623 /*@
9624   DMPlexCheckOrphanVertices - Check that no vertices are disconnected from the mesh, unless the mesh only consists of disconnected vertices.
9625 
9626   Collective
9627 
9628   Input Parameter:
9629 . dm - The `DMPLEX` object
9630 
9631   Level: developer
9632 
9633   Notes:
9634   This is mainly intended for debugging/testing purposes.
9635 
9636   Other cell types which are disconnected would be caught by the symmetry and face checks.
9637 
9638   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9639 
9640 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheck()`, `DMSetFromOptions()`
9641 @*/
9642 PetscErrorCode DMPlexCheckOrphanVertices(DM dm)
9643 {
9644   PetscInt pStart, pEnd, vStart, vEnd;
9645 
9646   PetscFunctionBegin;
9647   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9648   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9649   if (pStart == vStart && pEnd == vEnd) PetscFunctionReturn(PETSC_SUCCESS);
9650   for (PetscInt v = vStart; v < vEnd; ++v) {
9651     PetscInt suppSize;
9652 
9653     PetscCall(DMPlexGetSupportSize(dm, v, &suppSize));
9654     PetscCheck(suppSize, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Vertex %" PetscInt_FMT " is disconnected from the mesh", v);
9655   }
9656   PetscFunctionReturn(PETSC_SUCCESS);
9657 }
9658 
9659 /*@
9660   DMPlexCheck - Perform various checks of `DMPLEX` sanity
9661 
9662   Input Parameter:
9663 . dm - The `DMPLEX` object
9664 
9665   Level: developer
9666 
9667   Notes:
9668   This is a useful diagnostic when creating meshes programmatically.
9669 
9670   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9671 
9672   Currently does not include `DMPlexCheckCellShape()`.
9673 
9674 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9675 @*/
9676 PetscErrorCode DMPlexCheck(DM dm)
9677 {
9678   PetscInt cellHeight;
9679 
9680   PetscFunctionBegin;
9681   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9682   PetscCall(DMPlexCheckSymmetry(dm));
9683   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9684   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9685   PetscCall(DMPlexCheckGeometry(dm));
9686   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9687   PetscCall(DMPlexCheckInterfaceCones(dm));
9688   PetscCall(DMPlexCheckOrphanVertices(dm));
9689   PetscFunctionReturn(PETSC_SUCCESS);
9690 }
9691 
9692 typedef struct cell_stats {
9693   PetscReal min, max, sum, squaresum;
9694   PetscInt  count;
9695 } cell_stats_t;
9696 
9697 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9698 {
9699   PetscInt i, N = *len;
9700 
9701   for (i = 0; i < N; i++) {
9702     cell_stats_t *A = (cell_stats_t *)a;
9703     cell_stats_t *B = (cell_stats_t *)b;
9704 
9705     B->min = PetscMin(A->min, B->min);
9706     B->max = PetscMax(A->max, B->max);
9707     B->sum += A->sum;
9708     B->squaresum += A->squaresum;
9709     B->count += A->count;
9710   }
9711 }
9712 
9713 /*@
9714   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9715 
9716   Collective
9717 
9718   Input Parameters:
9719 + dm        - The `DMPLEX` object
9720 . output    - If true, statistics will be displayed on `stdout`
9721 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9722 
9723   Level: developer
9724 
9725   Notes:
9726   This is mainly intended for debugging/testing purposes.
9727 
9728   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9729 
9730 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9731 @*/
9732 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9733 {
9734   DM           dmCoarse;
9735   cell_stats_t stats, globalStats;
9736   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9737   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9738   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9739   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9740   PetscMPIInt  rank, size;
9741 
9742   PetscFunctionBegin;
9743   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9744   stats.min = PETSC_MAX_REAL;
9745   stats.max = PETSC_MIN_REAL;
9746   stats.sum = stats.squaresum = 0.;
9747   stats.count                 = 0;
9748 
9749   PetscCallMPI(MPI_Comm_size(comm, &size));
9750   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9751   PetscCall(DMGetCoordinateDim(dm, &cdim));
9752   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9753   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9754   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9755   for (c = cStart; c < cEnd; c++) {
9756     PetscInt  i;
9757     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9758 
9759     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9760     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9761     for (i = 0; i < PetscSqr(cdim); ++i) {
9762       frobJ += J[i] * J[i];
9763       frobInvJ += invJ[i] * invJ[i];
9764     }
9765     cond2 = frobJ * frobInvJ;
9766     cond  = PetscSqrtReal(cond2);
9767 
9768     stats.min = PetscMin(stats.min, cond);
9769     stats.max = PetscMax(stats.max, cond);
9770     stats.sum += cond;
9771     stats.squaresum += cond2;
9772     stats.count++;
9773     if (output && cond > limit) {
9774       PetscSection coordSection;
9775       Vec          coordsLocal;
9776       PetscScalar *coords = NULL;
9777       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9778 
9779       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9780       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9781       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9782       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9783       for (i = 0; i < Nv / cdim; ++i) {
9784         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9785         for (d = 0; d < cdim; ++d) {
9786           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9787           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9788         }
9789         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9790       }
9791       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9792       for (cl = 0; cl < clSize * 2; cl += 2) {
9793         const PetscInt edge = closure[cl];
9794 
9795         if ((edge >= eStart) && (edge < eEnd)) {
9796           PetscReal len;
9797 
9798           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9799           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9800         }
9801       }
9802       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9803       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9804     }
9805   }
9806   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9807 
9808   if (size > 1) {
9809     PetscMPIInt  blockLengths[2] = {4, 1};
9810     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9811     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9812     MPI_Op       statReduce;
9813 
9814     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9815     PetscCallMPI(MPI_Type_commit(&statType));
9816     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9817     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9818     PetscCallMPI(MPI_Op_free(&statReduce));
9819     PetscCallMPI(MPI_Type_free(&statType));
9820   } else {
9821     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9822   }
9823   if (rank == 0) {
9824     count = globalStats.count;
9825     min   = globalStats.min;
9826     max   = globalStats.max;
9827     mean  = globalStats.sum / globalStats.count;
9828     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9829   }
9830 
9831   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));
9832   PetscCall(PetscFree2(J, invJ));
9833 
9834   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9835   if (dmCoarse) {
9836     PetscBool isplex;
9837 
9838     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9839     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9840   }
9841   PetscFunctionReturn(PETSC_SUCCESS);
9842 }
9843 
9844 /*@
9845   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9846   orthogonal quality below given tolerance.
9847 
9848   Collective
9849 
9850   Input Parameters:
9851 + dm   - The `DMPLEX` object
9852 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9853 - atol - [0, 1] Absolute tolerance for tagging cells.
9854 
9855   Output Parameters:
9856 + OrthQual      - `Vec` containing orthogonal quality per cell
9857 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9858 
9859   Options Database Keys:
9860 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9861 - -dm_plex_orthogonal_quality_vec_view   - view OrthQual vector.
9862 
9863   Level: intermediate
9864 
9865   Notes:
9866   Orthogonal quality is given by the following formula\:
9867 
9868   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9869 
9870   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
9871   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9872   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9873   calculating the cosine of the angle between these vectors.
9874 
9875   Orthogonal quality ranges from 1 (best) to 0 (worst).
9876 
9877   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9878   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9879 
9880   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9881 
9882 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9883 @*/
9884 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9885 {
9886   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9887   PetscInt              *idx;
9888   PetscScalar           *oqVals;
9889   const PetscScalar     *cellGeomArr, *faceGeomArr;
9890   PetscReal             *ci, *fi, *Ai;
9891   MPI_Comm               comm;
9892   Vec                    cellgeom, facegeom;
9893   DM                     dmFace, dmCell;
9894   IS                     glob;
9895   ISLocalToGlobalMapping ltog;
9896   PetscViewer            vwr;
9897 
9898   PetscFunctionBegin;
9899   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9900   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9901   PetscAssertPointer(OrthQual, 4);
9902   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9903   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9904   PetscCall(DMGetDimension(dm, &nc));
9905   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9906   {
9907     DMPlexInterpolatedFlag interpFlag;
9908 
9909     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9910     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9911       PetscMPIInt rank;
9912 
9913       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9914       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9915     }
9916   }
9917   if (OrthQualLabel) {
9918     PetscAssertPointer(OrthQualLabel, 5);
9919     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9920     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9921   } else {
9922     *OrthQualLabel = NULL;
9923   }
9924   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9925   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9926   PetscCall(DMPlexCreateCellNumbering(dm, PETSC_TRUE, &glob));
9927   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9928   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9929   PetscCall(VecCreate(comm, OrthQual));
9930   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9931   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9932   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9933   PetscCall(VecSetUp(*OrthQual));
9934   PetscCall(ISDestroy(&glob));
9935   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9936   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9937   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9938   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9939   PetscCall(VecGetDM(cellgeom, &dmCell));
9940   PetscCall(VecGetDM(facegeom, &dmFace));
9941   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9942   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
9943     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9944     PetscInt         cellarr[2], *adj = NULL;
9945     PetscScalar     *cArr, *fArr;
9946     PetscReal        minvalc = 1.0, minvalf = 1.0;
9947     PetscFVCellGeom *cg;
9948 
9949     idx[cellIter] = cell - cStart;
9950     cellarr[0]    = cell;
9951     /* Make indexing into cellGeom easier */
9952     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9953     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9954     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9955     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9956     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
9957       PetscInt         i;
9958       const PetscInt   neigh  = adj[cellneigh];
9959       PetscReal        normci = 0, normfi = 0, normai = 0;
9960       PetscFVCellGeom *cgneigh;
9961       PetscFVFaceGeom *fg;
9962 
9963       /* Don't count ourselves in the neighbor list */
9964       if (neigh == cell) continue;
9965       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9966       cellarr[1] = neigh;
9967       {
9968         PetscInt        numcovpts;
9969         const PetscInt *covpts;
9970 
9971         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9972         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9973         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9974       }
9975 
9976       /* Compute c_i, f_i and their norms */
9977       for (i = 0; i < nc; i++) {
9978         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9979         fi[i] = fg->centroid[i] - cg->centroid[i];
9980         Ai[i] = fg->normal[i];
9981         normci += PetscPowReal(ci[i], 2);
9982         normfi += PetscPowReal(fi[i], 2);
9983         normai += PetscPowReal(Ai[i], 2);
9984       }
9985       normci = PetscSqrtReal(normci);
9986       normfi = PetscSqrtReal(normfi);
9987       normai = PetscSqrtReal(normai);
9988 
9989       /* Normalize and compute for each face-cell-normal pair */
9990       for (i = 0; i < nc; i++) {
9991         ci[i] = ci[i] / normci;
9992         fi[i] = fi[i] / normfi;
9993         Ai[i] = Ai[i] / normai;
9994         /* PetscAbs because I don't know if normals are guaranteed to point out */
9995         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
9996         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
9997       }
9998       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
9999       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
10000     }
10001     PetscCall(PetscFree(adj));
10002     PetscCall(PetscFree2(cArr, fArr));
10003     /* Defer to cell if they're equal */
10004     oqVals[cellIter] = PetscMin(minvalf, minvalc);
10005     if (OrthQualLabel) {
10006       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
10007     }
10008   }
10009   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
10010   PetscCall(VecAssemblyBegin(*OrthQual));
10011   PetscCall(VecAssemblyEnd(*OrthQual));
10012   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
10013   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
10014   PetscCall(PetscOptionsCreateViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
10015   if (OrthQualLabel) {
10016     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
10017   }
10018   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
10019   PetscCall(PetscViewerDestroy(&vwr));
10020   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
10021   PetscFunctionReturn(PETSC_SUCCESS);
10022 }
10023 
10024 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
10025  * interpolator construction */
10026 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
10027 {
10028   PetscSection section, newSection, gsection;
10029   PetscSF      sf;
10030   PetscBool    hasConstraints, ghasConstraints;
10031 
10032   PetscFunctionBegin;
10033   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10034   PetscAssertPointer(odm, 2);
10035   PetscCall(DMGetLocalSection(dm, &section));
10036   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
10037   PetscCallMPI(MPIU_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
10038   if (!ghasConstraints) {
10039     PetscCall(PetscObjectReference((PetscObject)dm));
10040     *odm = dm;
10041     PetscFunctionReturn(PETSC_SUCCESS);
10042   }
10043   PetscCall(DMClone(dm, odm));
10044   PetscCall(DMCopyFields(dm, PETSC_DETERMINE, PETSC_DETERMINE, *odm));
10045   PetscCall(DMGetLocalSection(*odm, &newSection));
10046   PetscCall(DMGetPointSF(*odm, &sf));
10047   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_TRUE, PETSC_FALSE, &gsection));
10048   PetscCall(DMSetGlobalSection(*odm, gsection));
10049   PetscCall(PetscSectionDestroy(&gsection));
10050   PetscFunctionReturn(PETSC_SUCCESS);
10051 }
10052 
10053 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
10054 {
10055   DM        dmco, dmfo;
10056   Mat       interpo;
10057   Vec       rscale;
10058   Vec       cglobalo, clocal;
10059   Vec       fglobal, fglobalo, flocal;
10060   PetscBool regular;
10061 
10062   PetscFunctionBegin;
10063   PetscCall(DMGetFullDM(dmc, &dmco));
10064   PetscCall(DMGetFullDM(dmf, &dmfo));
10065   PetscCall(DMSetCoarseDM(dmfo, dmco));
10066   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
10067   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
10068   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
10069   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
10070   PetscCall(DMCreateLocalVector(dmc, &clocal));
10071   PetscCall(VecSet(cglobalo, 0.));
10072   PetscCall(VecSet(clocal, 0.));
10073   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
10074   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
10075   PetscCall(DMCreateLocalVector(dmf, &flocal));
10076   PetscCall(VecSet(fglobal, 0.));
10077   PetscCall(VecSet(fglobalo, 0.));
10078   PetscCall(VecSet(flocal, 0.));
10079   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
10080   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
10081   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
10082   PetscCall(MatMult(interpo, cglobalo, fglobalo));
10083   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
10084   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
10085   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
10086   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
10087   *shift = fglobal;
10088   PetscCall(VecDestroy(&flocal));
10089   PetscCall(VecDestroy(&fglobalo));
10090   PetscCall(VecDestroy(&clocal));
10091   PetscCall(VecDestroy(&cglobalo));
10092   PetscCall(VecDestroy(&rscale));
10093   PetscCall(MatDestroy(&interpo));
10094   PetscCall(DMDestroy(&dmfo));
10095   PetscCall(DMDestroy(&dmco));
10096   PetscFunctionReturn(PETSC_SUCCESS);
10097 }
10098 
10099 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
10100 {
10101   PetscObject shifto;
10102   Vec         shift;
10103 
10104   PetscFunctionBegin;
10105   if (!interp) {
10106     Vec rscale;
10107 
10108     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
10109     PetscCall(VecDestroy(&rscale));
10110   } else {
10111     PetscCall(PetscObjectReference((PetscObject)interp));
10112   }
10113   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
10114   if (!shifto) {
10115     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
10116     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
10117     shifto = (PetscObject)shift;
10118     PetscCall(VecDestroy(&shift));
10119   }
10120   shift = (Vec)shifto;
10121   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
10122   PetscCall(VecAXPY(fineSol, 1.0, shift));
10123   PetscCall(MatDestroy(&interp));
10124   PetscFunctionReturn(PETSC_SUCCESS);
10125 }
10126 
10127 /* Pointwise interpolation
10128      Just code FEM for now
10129      u^f = I u^c
10130      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
10131      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
10132      I_{ij} = psi^f_i phi^c_j
10133 */
10134 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
10135 {
10136   PetscSection gsc, gsf;
10137   PetscInt     m, n;
10138   void        *ctx;
10139   DM           cdm;
10140   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
10141 
10142   PetscFunctionBegin;
10143   PetscCall(DMGetGlobalSection(dmFine, &gsf));
10144   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10145   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10146   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10147 
10148   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
10149   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
10150   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10151   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
10152   PetscCall(DMGetApplicationContext(dmFine, &ctx));
10153 
10154   PetscCall(DMGetCoarseDM(dmFine, &cdm));
10155   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10156   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
10157   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
10158   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
10159   if (scaling) {
10160     /* Use naive scaling */
10161     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
10162   }
10163   PetscFunctionReturn(PETSC_SUCCESS);
10164 }
10165 
10166 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
10167 {
10168   VecScatter ctx;
10169 
10170   PetscFunctionBegin;
10171   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
10172   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
10173   PetscCall(VecScatterDestroy(&ctx));
10174   PetscFunctionReturn(PETSC_SUCCESS);
10175 }
10176 
10177 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[])
10178 {
10179   const PetscInt f  = (PetscInt)PetscRealPart(constants[numConstants]);
10180   const PetscInt Nc = uOff[f + 1] - uOff[f];
10181   for (PetscInt c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
10182 }
10183 
10184 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *lmass, Vec *mass)
10185 {
10186   DM           dmc;
10187   PetscDS      ds;
10188   Vec          ones, locmass;
10189   IS           cellIS;
10190   PetscFormKey key;
10191   PetscInt     depth;
10192 
10193   PetscFunctionBegin;
10194   PetscCall(DMClone(dm, &dmc));
10195   PetscCall(DMCopyDisc(dm, dmc));
10196   PetscCall(DMGetDS(dmc, &ds));
10197   for (PetscInt f = 0; f < dmc->Nf; ++f) PetscCall(PetscDSSetJacobian(ds, f, f, g0_identity_private, NULL, NULL, NULL));
10198   if (mass) PetscCall(DMCreateGlobalVector(dm, mass));
10199   if (lmass) PetscCall(DMCreateLocalVector(dm, &locmass));
10200   else PetscCall(DMGetLocalVector(dm, &locmass));
10201   PetscCall(DMGetLocalVector(dm, &ones));
10202   PetscCall(DMPlexGetDepth(dm, &depth));
10203   PetscCall(DMGetStratumIS(dm, "depth", depth, &cellIS));
10204   PetscCall(VecSet(locmass, 0.0));
10205   PetscCall(VecSet(ones, 1.0));
10206   key.label = NULL;
10207   key.value = 0;
10208   key.field = 0;
10209   key.part  = 0;
10210   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
10211   PetscCall(ISDestroy(&cellIS));
10212   if (mass) {
10213     PetscCall(DMLocalToGlobalBegin(dm, locmass, ADD_VALUES, *mass));
10214     PetscCall(DMLocalToGlobalEnd(dm, locmass, ADD_VALUES, *mass));
10215   }
10216   PetscCall(DMRestoreLocalVector(dm, &ones));
10217   if (lmass) *lmass = locmass;
10218   else PetscCall(DMRestoreLocalVector(dm, &locmass));
10219   PetscCall(DMDestroy(&dmc));
10220   PetscFunctionReturn(PETSC_SUCCESS);
10221 }
10222 
10223 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
10224 {
10225   PetscSection gsc, gsf;
10226   PetscInt     m, n;
10227   void        *ctx;
10228   DM           cdm;
10229   PetscBool    regular;
10230 
10231   PetscFunctionBegin;
10232   if (dmFine == dmCoarse) {
10233     DM            dmc;
10234     PetscDS       ds;
10235     PetscWeakForm wf;
10236     Vec           u;
10237     IS            cellIS;
10238     PetscFormKey  key;
10239     PetscInt      depth;
10240 
10241     PetscCall(DMClone(dmFine, &dmc));
10242     PetscCall(DMCopyDisc(dmFine, dmc));
10243     PetscCall(DMGetDS(dmc, &ds));
10244     PetscCall(PetscDSGetWeakForm(ds, &wf));
10245     PetscCall(PetscWeakFormClear(wf));
10246     for (PetscInt f = 0; f < dmc->Nf; ++f) PetscCall(PetscDSSetJacobian(ds, f, f, g0_identity_private, NULL, NULL, NULL));
10247     PetscCall(DMCreateMatrix(dmc, mass));
10248     PetscCall(DMGetLocalVector(dmc, &u));
10249     PetscCall(DMPlexGetDepth(dmc, &depth));
10250     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
10251     PetscCall(MatZeroEntries(*mass));
10252     key.label = NULL;
10253     key.value = 0;
10254     key.field = 0;
10255     key.part  = 0;
10256     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
10257     PetscCall(ISDestroy(&cellIS));
10258     PetscCall(DMRestoreLocalVector(dmc, &u));
10259     PetscCall(DMDestroy(&dmc));
10260   } else {
10261     PetscCall(DMGetGlobalSection(dmFine, &gsf));
10262     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10263     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10264     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10265 
10266     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
10267     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10268     PetscCall(MatSetType(*mass, dmCoarse->mattype));
10269     PetscCall(DMGetApplicationContext(dmFine, &ctx));
10270 
10271     PetscCall(DMGetCoarseDM(dmFine, &cdm));
10272     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10273     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
10274     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
10275   }
10276   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
10277   PetscFunctionReturn(PETSC_SUCCESS);
10278 }
10279 
10280 /*@
10281   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10282 
10283   Input Parameter:
10284 . dm - The `DMPLEX` object
10285 
10286   Output Parameter:
10287 . regular - The flag
10288 
10289   Level: intermediate
10290 
10291 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
10292 @*/
10293 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
10294 {
10295   PetscFunctionBegin;
10296   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10297   PetscAssertPointer(regular, 2);
10298   *regular = ((DM_Plex *)dm->data)->regularRefinement;
10299   PetscFunctionReturn(PETSC_SUCCESS);
10300 }
10301 
10302 /*@
10303   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10304 
10305   Input Parameters:
10306 + dm      - The `DMPLEX` object
10307 - regular - The flag
10308 
10309   Level: intermediate
10310 
10311 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
10312 @*/
10313 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
10314 {
10315   PetscFunctionBegin;
10316   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10317   ((DM_Plex *)dm->data)->regularRefinement = regular;
10318   PetscFunctionReturn(PETSC_SUCCESS);
10319 }
10320 
10321 /*@
10322   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
10323   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
10324 
10325   Not Collective
10326 
10327   Input Parameter:
10328 . dm - The `DMPLEX` object
10329 
10330   Output Parameters:
10331 + anchorSection - If not `NULL`, set to the section describing which points anchor the constrained points.
10332 - anchorIS      - If not `NULL`, set to the list of anchors indexed by `anchorSection`
10333 
10334   Level: intermediate
10335 
10336 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
10337 @*/
10338 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
10339 {
10340   DM_Plex *plex = (DM_Plex *)dm->data;
10341 
10342   PetscFunctionBegin;
10343   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10344   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
10345   if (anchorSection) *anchorSection = plex->anchorSection;
10346   if (anchorIS) *anchorIS = plex->anchorIS;
10347   PetscFunctionReturn(PETSC_SUCCESS);
10348 }
10349 
10350 /*@
10351   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.
10352 
10353   Collective
10354 
10355   Input Parameters:
10356 + dm            - The `DMPLEX` object
10357 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
10358                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10359 - anchorIS      - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10360 
10361   Level: intermediate
10362 
10363   Notes:
10364   Unlike boundary conditions, when a point's degrees of freedom in a section are constrained to
10365   an outside value, the anchor constraints set a point's degrees of freedom to be a linear
10366   combination of other points' degrees of freedom.
10367 
10368   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
10369   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
10370 
10371   The reference counts of `anchorSection` and `anchorIS` are incremented.
10372 
10373 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
10374 @*/
10375 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
10376 {
10377   DM_Plex    *plex = (DM_Plex *)dm->data;
10378   PetscMPIInt result;
10379 
10380   PetscFunctionBegin;
10381   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10382   if (anchorSection) {
10383     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
10384     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
10385     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
10386   }
10387   if (anchorIS) {
10388     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
10389     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
10390     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
10391   }
10392 
10393   PetscCall(PetscObjectReference((PetscObject)anchorSection));
10394   PetscCall(PetscSectionDestroy(&plex->anchorSection));
10395   plex->anchorSection = anchorSection;
10396 
10397   PetscCall(PetscObjectReference((PetscObject)anchorIS));
10398   PetscCall(ISDestroy(&plex->anchorIS));
10399   plex->anchorIS = anchorIS;
10400 
10401   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
10402     PetscInt        size, a, pStart, pEnd;
10403     const PetscInt *anchors;
10404 
10405     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10406     PetscCall(ISGetLocalSize(anchorIS, &size));
10407     PetscCall(ISGetIndices(anchorIS, &anchors));
10408     for (a = 0; a < size; a++) {
10409       PetscInt p;
10410 
10411       p = anchors[a];
10412       if (p >= pStart && p < pEnd) {
10413         PetscInt dof;
10414 
10415         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10416         if (dof) {
10417           PetscCall(ISRestoreIndices(anchorIS, &anchors));
10418           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
10419         }
10420       }
10421     }
10422     PetscCall(ISRestoreIndices(anchorIS, &anchors));
10423   }
10424   /* reset the generic constraints */
10425   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
10426   PetscFunctionReturn(PETSC_SUCCESS);
10427 }
10428 
10429 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
10430 {
10431   PetscSection anchorSection;
10432   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
10433 
10434   PetscFunctionBegin;
10435   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10436   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10437   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
10438   PetscCall(PetscSectionGetNumFields(section, &numFields));
10439   if (numFields) {
10440     PetscInt f;
10441     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
10442 
10443     for (f = 0; f < numFields; f++) {
10444       PetscInt numComp;
10445 
10446       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
10447       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
10448     }
10449   }
10450   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10451   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10452   pStart = PetscMax(pStart, sStart);
10453   pEnd   = PetscMin(pEnd, sEnd);
10454   pEnd   = PetscMax(pStart, pEnd);
10455   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
10456   for (p = pStart; p < pEnd; p++) {
10457     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10458     if (dof) {
10459       PetscCall(PetscSectionGetDof(section, p, &dof));
10460       PetscCall(PetscSectionSetDof(*cSec, p, dof));
10461       for (f = 0; f < numFields; f++) {
10462         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
10463         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
10464       }
10465     }
10466   }
10467   PetscCall(PetscSectionSetUp(*cSec));
10468   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
10469   PetscFunctionReturn(PETSC_SUCCESS);
10470 }
10471 
10472 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
10473 {
10474   PetscSection    aSec;
10475   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
10476   const PetscInt *anchors;
10477   PetscInt        numFields, f;
10478   IS              aIS;
10479   MatType         mtype;
10480   PetscBool       iscuda, iskokkos;
10481 
10482   PetscFunctionBegin;
10483   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10484   PetscCall(PetscSectionGetStorageSize(cSec, &m));
10485   PetscCall(PetscSectionGetStorageSize(section, &n));
10486   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
10487   PetscCall(MatSetSizes(*cMat, m, n, m, n));
10488   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
10489   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
10490   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
10491   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
10492   if (iscuda) mtype = MATSEQAIJCUSPARSE;
10493   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
10494   else mtype = MATSEQAIJ;
10495   PetscCall(MatSetType(*cMat, mtype));
10496   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
10497   PetscCall(ISGetIndices(aIS, &anchors));
10498   /* cSec will be a subset of aSec and section */
10499   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
10500   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10501   PetscCall(PetscMalloc1(m + 1, &i));
10502   i[0] = 0;
10503   PetscCall(PetscSectionGetNumFields(section, &numFields));
10504   for (p = pStart; p < pEnd; p++) {
10505     PetscInt rDof, rOff, r;
10506 
10507     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10508     if (!rDof) continue;
10509     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10510     if (numFields) {
10511       for (f = 0; f < numFields; f++) {
10512         annz = 0;
10513         for (r = 0; r < rDof; r++) {
10514           a = anchors[rOff + r];
10515           if (a < sStart || a >= sEnd) continue;
10516           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10517           annz += aDof;
10518         }
10519         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10520         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
10521         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10522       }
10523     } else {
10524       annz = 0;
10525       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10526       for (q = 0; q < dof; q++) {
10527         a = anchors[rOff + q];
10528         if (a < sStart || a >= sEnd) continue;
10529         PetscCall(PetscSectionGetDof(section, a, &aDof));
10530         annz += aDof;
10531       }
10532       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10533       PetscCall(PetscSectionGetOffset(cSec, p, &off));
10534       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10535     }
10536   }
10537   nnz = i[m];
10538   PetscCall(PetscMalloc1(nnz, &j));
10539   offset = 0;
10540   for (p = pStart; p < pEnd; p++) {
10541     if (numFields) {
10542       for (f = 0; f < numFields; f++) {
10543         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10544         for (q = 0; q < dof; q++) {
10545           PetscInt rDof, rOff, r;
10546           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10547           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10548           for (r = 0; r < rDof; r++) {
10549             PetscInt s;
10550 
10551             a = anchors[rOff + r];
10552             if (a < sStart || a >= sEnd) continue;
10553             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10554             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
10555             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10556           }
10557         }
10558       }
10559     } else {
10560       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10561       for (q = 0; q < dof; q++) {
10562         PetscInt rDof, rOff, r;
10563         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10564         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10565         for (r = 0; r < rDof; r++) {
10566           PetscInt s;
10567 
10568           a = anchors[rOff + r];
10569           if (a < sStart || a >= sEnd) continue;
10570           PetscCall(PetscSectionGetDof(section, a, &aDof));
10571           PetscCall(PetscSectionGetOffset(section, a, &aOff));
10572           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10573         }
10574       }
10575     }
10576   }
10577   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
10578   PetscCall(PetscFree(i));
10579   PetscCall(PetscFree(j));
10580   PetscCall(ISRestoreIndices(aIS, &anchors));
10581   PetscFunctionReturn(PETSC_SUCCESS);
10582 }
10583 
10584 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
10585 {
10586   DM_Plex     *plex = (DM_Plex *)dm->data;
10587   PetscSection anchorSection, section, cSec;
10588   Mat          cMat;
10589 
10590   PetscFunctionBegin;
10591   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10592   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10593   if (anchorSection) {
10594     PetscInt Nf;
10595 
10596     PetscCall(DMGetLocalSection(dm, &section));
10597     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
10598     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
10599     PetscCall(DMGetNumFields(dm, &Nf));
10600     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
10601     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
10602     PetscCall(PetscSectionDestroy(&cSec));
10603     PetscCall(MatDestroy(&cMat));
10604   }
10605   PetscFunctionReturn(PETSC_SUCCESS);
10606 }
10607 
10608 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10609 {
10610   IS           subis;
10611   PetscSection section, subsection;
10612 
10613   PetscFunctionBegin;
10614   PetscCall(DMGetLocalSection(dm, &section));
10615   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10616   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10617   /* Create subdomain */
10618   PetscCall(DMPlexFilter(dm, label, value, PETSC_FALSE, PETSC_FALSE, NULL, subdm));
10619   /* Create submodel */
10620   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10621   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10622   PetscCall(DMSetLocalSection(*subdm, subsection));
10623   PetscCall(PetscSectionDestroy(&subsection));
10624   PetscCall(DMCopyDisc(dm, *subdm));
10625   /* Create map from submodel to global model */
10626   if (is) {
10627     PetscSection    sectionGlobal, subsectionGlobal;
10628     IS              spIS;
10629     const PetscInt *spmap;
10630     PetscInt       *subIndices;
10631     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10632     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10633 
10634     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10635     PetscCall(ISGetIndices(spIS, &spmap));
10636     PetscCall(PetscSectionGetNumFields(section, &Nf));
10637     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10638     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10639     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10640     for (p = pStart; p < pEnd; ++p) {
10641       PetscInt gdof, pSubSize = 0;
10642 
10643       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10644       if (gdof > 0) {
10645         for (f = 0; f < Nf; ++f) {
10646           PetscInt fdof, fcdof;
10647 
10648           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10649           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10650           pSubSize += fdof - fcdof;
10651         }
10652         subSize += pSubSize;
10653         if (pSubSize) {
10654           if (bs < 0) {
10655             bs = pSubSize;
10656           } else if (bs != pSubSize) {
10657             /* Layout does not admit a pointwise block size */
10658             bs = 1;
10659           }
10660         }
10661       }
10662     }
10663     /* Must have same blocksize on all procs (some might have no points) */
10664     bsLocal[0] = bs < 0 ? PETSC_INT_MAX : bs;
10665     bsLocal[1] = bs;
10666     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10667     if (bsMinMax[0] != bsMinMax[1]) {
10668       bs = 1;
10669     } else {
10670       bs = bsMinMax[0];
10671     }
10672     PetscCall(PetscMalloc1(subSize, &subIndices));
10673     for (p = pStart; p < pEnd; ++p) {
10674       PetscInt gdof, goff;
10675 
10676       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10677       if (gdof > 0) {
10678         const PetscInt point = spmap[p];
10679 
10680         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10681         for (f = 0; f < Nf; ++f) {
10682           PetscInt fdof, fcdof, fc, f2, poff = 0;
10683 
10684           /* Can get rid of this loop by storing field information in the global section */
10685           for (f2 = 0; f2 < f; ++f2) {
10686             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10687             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10688             poff += fdof - fcdof;
10689           }
10690           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10691           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10692           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10693         }
10694       }
10695     }
10696     PetscCall(ISRestoreIndices(spIS, &spmap));
10697     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10698     if (bs > 1) {
10699       /* We need to check that the block size does not come from non-contiguous fields */
10700       PetscInt i, j, set = 1;
10701       for (i = 0; i < subSize; i += bs) {
10702         for (j = 0; j < bs; ++j) {
10703           if (subIndices[i + j] != subIndices[i] + j) {
10704             set = 0;
10705             break;
10706           }
10707         }
10708       }
10709       if (set) PetscCall(ISSetBlockSize(*is, bs));
10710     }
10711     /* Attach nullspace */
10712     for (f = 0; f < Nf; ++f) {
10713       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10714       if ((*subdm)->nullspaceConstructors[f]) break;
10715     }
10716     if (f < Nf) {
10717       MatNullSpace nullSpace;
10718       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10719 
10720       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10721       PetscCall(MatNullSpaceDestroy(&nullSpace));
10722     }
10723   }
10724   PetscFunctionReturn(PETSC_SUCCESS);
10725 }
10726 
10727 /*@
10728   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10729 
10730   Input Parameters:
10731 + dm    - The `DM`
10732 - dummy - unused argument
10733 
10734   Options Database Key:
10735 . -dm_plex_monitor_throughput - Activate the monitor
10736 
10737   Level: developer
10738 
10739 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10740 @*/
10741 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10742 {
10743   PetscLogHandler default_handler;
10744 
10745   PetscFunctionBegin;
10746   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10747   PetscCall(PetscLogGetDefaultHandler(&default_handler));
10748   if (default_handler) {
10749     PetscLogEvent      event;
10750     PetscEventPerfInfo eventInfo;
10751     PetscReal          cellRate, flopRate;
10752     PetscInt           cStart, cEnd, Nf, N;
10753     const char        *name;
10754 
10755     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10756     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10757     PetscCall(DMGetNumFields(dm, &Nf));
10758     PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10759     PetscCall(PetscLogEventGetPerfInfo(PETSC_DEFAULT, event, &eventInfo));
10760     N        = (cEnd - cStart) * Nf * eventInfo.count;
10761     flopRate = eventInfo.flops / eventInfo.time;
10762     cellRate = N / eventInfo.time;
10763     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)));
10764   } else {
10765     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.");
10766   }
10767   PetscFunctionReturn(PETSC_SUCCESS);
10768 }
10769