xref: /petsc/src/dm/impls/plex/plex.c (revision 472b984436794f5b5f82c9039f5d768939e601a1)
1 #include <petsc/private/dmpleximpl.h> /*I      "petscdmplex.h"   I*/
2 #include <petsc/private/dmlabelimpl.h>
3 #include <petsc/private/isimpl.h>
4 #include <petsc/private/vecimpl.h>
5 #include <petsc/private/glvisvecimpl.h>
6 #include <petscsf.h>
7 #include <petscds.h>
8 #include <petscdraw.h>
9 #include <petscdmfield.h>
10 #include <petscdmplextransform.h>
11 #include <petscblaslapack.h>
12 
13 /* Logging support */
14 PetscLogEvent DMPLEX_Interpolate, DMPLEX_Partition, DMPLEX_Distribute, DMPLEX_DistributeCones, DMPLEX_DistributeLabels, DMPLEX_DistributeSF, DMPLEX_DistributeOverlap, DMPLEX_DistributeField, DMPLEX_DistributeData, DMPLEX_Migrate, DMPLEX_InterpolateSF, DMPLEX_GlobalToNaturalBegin, DMPLEX_GlobalToNaturalEnd, DMPLEX_NaturalToGlobalBegin, DMPLEX_NaturalToGlobalEnd, DMPLEX_Stratify, DMPLEX_Symmetrize, DMPLEX_Preallocate, DMPLEX_ResidualFEM, DMPLEX_JacobianFEM, DMPLEX_InterpolatorFEM, DMPLEX_InjectorFEM, DMPLEX_IntegralFEM, DMPLEX_CreateGmsh, DMPLEX_RebalanceSharedPoints, DMPLEX_PartSelf, DMPLEX_PartLabelInvert, DMPLEX_PartLabelCreateSF, DMPLEX_PartStratSF, DMPLEX_CreatePointSF, DMPLEX_LocatePoints, DMPLEX_TopologyView, DMPLEX_LabelsView, DMPLEX_CoordinatesView, DMPLEX_SectionView, DMPLEX_GlobalVectorView, DMPLEX_LocalVectorView, DMPLEX_TopologyLoad, DMPLEX_LabelsLoad, DMPLEX_CoordinatesLoad, DMPLEX_SectionLoad, DMPLEX_GlobalVectorLoad, DMPLEX_LocalVectorLoad;
15 PetscLogEvent DMPLEX_RebalBuildGraph, DMPLEX_RebalRewriteSF, DMPLEX_RebalGatherGraph, DMPLEX_RebalPartition, DMPLEX_RebalScatterPart, DMPLEX_Generate, DMPLEX_Transform, DMPLEX_GetLocalOffsets, DMPLEX_Uninterpolate;
16 
17 PetscBool  Plexcite       = PETSC_FALSE;
18 const char PlexCitation[] = "@article{LangeMitchellKnepleyGorman2015,\n"
19                             "title     = {Efficient mesh management in {Firedrake} using {PETSc-DMPlex}},\n"
20                             "author    = {Michael Lange and Lawrence Mitchell and Matthew G. Knepley and Gerard J. Gorman},\n"
21                             "journal   = {SIAM Journal on Scientific Computing},\n"
22                             "volume    = {38},\n"
23                             "number    = {5},\n"
24                             "pages     = {S143--S155},\n"
25                             "eprint    = {http://arxiv.org/abs/1506.07749},\n"
26                             "doi       = {10.1137/15M1026092},\n"
27                             "year      = {2016},\n"
28                             "petsc_uses={DMPlex},\n}\n";
29 
30 PETSC_EXTERN PetscErrorCode VecView_MPI(Vec, PetscViewer);
31 
32 /*@
33   DMPlexIsSimplex - Is the first cell in this mesh a simplex?
34 
35   Input Parameter:
36 . dm - The `DMPLEX` object
37 
38   Output Parameter:
39 . simplex - Flag checking for a simplex
40 
41   Level: intermediate
42 
43   Note:
44   This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
45   If the mesh has no cells, this returns `PETSC_FALSE`.
46 
47 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSimplexOrBoxCells()`, `DMPlexGetCellType()`, `DMPlexGetHeightStratum()`, `DMPolytopeTypeGetNumVertices()`
48 @*/
49 PetscErrorCode DMPlexIsSimplex(DM dm, PetscBool *simplex)
50 {
51   DMPolytopeType ct;
52   PetscInt       cStart, cEnd;
53 
54   PetscFunctionBegin;
55   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
56   if (cEnd <= cStart) {
57     *simplex = PETSC_FALSE;
58     PetscFunctionReturn(PETSC_SUCCESS);
59   }
60   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
61   *simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
62   PetscFunctionReturn(PETSC_SUCCESS);
63 }
64 
65 /*@
66   DMPlexGetSimplexOrBoxCells - Get the range of cells which are neither prisms nor ghost FV cells
67 
68   Input Parameters:
69 + dm     - The `DMPLEX` object
70 - height - The cell height in the Plex, 0 is the default
71 
72   Output Parameters:
73 + cStart - The first "normal" cell
74 - cEnd   - The upper bound on "normal" cells
75 
76   Level: developer
77 
78   Note:
79   This function requires that tensor cells are ordered last.
80 
81 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetCellTypeStratum()`
82 @*/
83 PetscErrorCode DMPlexGetSimplexOrBoxCells(DM dm, PetscInt height, PetscInt *cStart, PetscInt *cEnd)
84 {
85   DMLabel         ctLabel;
86   IS              valueIS;
87   const PetscInt *ctypes;
88   PetscBool       found = PETSC_FALSE;
89   PetscInt        Nct, cS = PETSC_MAX_INT, cE = 0;
90 
91   PetscFunctionBegin;
92   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
93   PetscCall(DMLabelGetValueIS(ctLabel, &valueIS));
94   PetscCall(ISGetLocalSize(valueIS, &Nct));
95   PetscCall(ISGetIndices(valueIS, &ctypes));
96   for (PetscInt t = 0; t < Nct; ++t) {
97     const DMPolytopeType ct = (DMPolytopeType)ctypes[t];
98     PetscInt             ctS, ctE, ht;
99 
100     if (ct == DM_POLYTOPE_UNKNOWN) {
101       // If any cells are not typed, just use all cells
102       PetscCall(DMPlexGetHeightStratum(dm, PetscMax(height, 0), cStart, cEnd));
103       break;
104     }
105     if (DMPolytopeTypeIsHybrid(ct) || ct == DM_POLYTOPE_FV_GHOST) continue;
106     PetscCall(DMLabelGetStratumBounds(ctLabel, ct, &ctS, &ctE));
107     if (ctS >= ctE) continue;
108     // Check that a point has the right height
109     PetscCall(DMPlexGetPointHeight(dm, ctS, &ht));
110     if (ht != height) continue;
111     cS    = PetscMin(cS, ctS);
112     cE    = PetscMax(cE, ctE);
113     found = PETSC_TRUE;
114   }
115   if (!Nct || !found) cS = cE = 0;
116   PetscCall(ISDestroy(&valueIS));
117   // Reset label for fast lookup
118   PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
119   if (cStart) *cStart = cS;
120   if (cEnd) *cEnd = cE;
121   PetscFunctionReturn(PETSC_SUCCESS);
122 }
123 
124 PetscErrorCode DMPlexGetFieldTypes_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *types, PetscInt **ssStart, PetscInt **ssEnd, PetscViewerVTKFieldType **sft)
125 {
126   PetscInt                 cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd, c, depth, cellHeight, t;
127   PetscInt                *sStart, *sEnd;
128   PetscViewerVTKFieldType *ft;
129   PetscInt                 vcdof[DM_NUM_POLYTOPES + 1], globalvcdof[DM_NUM_POLYTOPES + 1];
130   DMLabel                  depthLabel, ctLabel;
131 
132   PetscFunctionBegin;
133   /* the vcdof and globalvcdof are sized to allow every polytope type and simple vertex at DM_NUM_POLYTOPES */
134   PetscCall(PetscArrayzero(vcdof, DM_NUM_POLYTOPES + 1));
135   PetscCall(DMGetCoordinateDim(dm, &cdim));
136   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
137   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
138   if (field >= 0) {
139     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[DM_NUM_POLYTOPES]));
140   } else {
141     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[DM_NUM_POLYTOPES]));
142   }
143 
144   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
145   PetscCall(DMPlexGetDepth(dm, &depth));
146   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
147   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
148   for (c = 0; c < DM_NUM_POLYTOPES; ++c) {
149     const DMPolytopeType ict = (DMPolytopeType)c;
150     PetscInt             dep;
151 
152     if (ict == DM_POLYTOPE_FV_GHOST) continue;
153     PetscCall(DMLabelGetStratumBounds(ctLabel, ict, &cStart, &cEnd));
154     if (pStart >= 0) {
155       PetscCall(DMLabelGetValue(depthLabel, cStart, &dep));
156       if (dep != depth - cellHeight) continue;
157     }
158     if (field >= 0) {
159       if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[c]));
160     } else {
161       if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[c]));
162     }
163   }
164 
165   PetscCall(MPIU_Allreduce(vcdof, globalvcdof, DM_NUM_POLYTOPES + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
166   *types = 0;
167 
168   for (c = 0; c < DM_NUM_POLYTOPES + 1; ++c) {
169     if (globalvcdof[c]) ++(*types);
170   }
171 
172   PetscCall(PetscMalloc3(*types, &sStart, *types, &sEnd, *types, &ft));
173   t = 0;
174   if (globalvcdof[DM_NUM_POLYTOPES]) {
175     sStart[t] = vStart;
176     sEnd[t]   = vEnd;
177     ft[t]     = (globalvcdof[t] == cdim) ? PETSC_VTK_POINT_VECTOR_FIELD : PETSC_VTK_POINT_FIELD;
178     ++t;
179   }
180 
181   for (c = 0; c < DM_NUM_POLYTOPES; ++c) {
182     if (globalvcdof[c]) {
183       const DMPolytopeType ict = (DMPolytopeType)c;
184 
185       PetscCall(DMLabelGetStratumBounds(ctLabel, ict, &cStart, &cEnd));
186       sStart[t] = cStart;
187       sEnd[t]   = cEnd;
188       ft[t]     = (globalvcdof[c] == cdim) ? PETSC_VTK_CELL_VECTOR_FIELD : PETSC_VTK_CELL_FIELD;
189       ++t;
190     }
191   }
192 
193   if (!*types) {
194     if (field >= 0) {
195       const char *fieldname;
196 
197       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
198       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
199     } else {
200       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
201     }
202   }
203 
204   *ssStart = sStart;
205   *ssEnd   = sEnd;
206   *sft     = ft;
207   PetscFunctionReturn(PETSC_SUCCESS);
208 }
209 
210 PetscErrorCode DMPlexRestoreFieldTypes_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *types, PetscInt **sStart, PetscInt **sEnd, PetscViewerVTKFieldType **ft)
211 {
212   PetscFunctionBegin;
213   PetscCall(PetscFree3(*sStart, *sEnd, *ft));
214   PetscFunctionReturn(PETSC_SUCCESS);
215 }
216 
217 PetscErrorCode DMPlexGetFieldType_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *sStart, PetscInt *sEnd, PetscViewerVTKFieldType *ft)
218 {
219   PetscInt cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd;
220   PetscInt vcdof[2] = {0, 0}, globalvcdof[2];
221 
222   PetscFunctionBegin;
223   *ft = PETSC_VTK_INVALID;
224   PetscCall(DMGetCoordinateDim(dm, &cdim));
225   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
226   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
227   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
228   if (field >= 0) {
229     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[0]));
230     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[1]));
231   } else {
232     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[0]));
233     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[1]));
234   }
235   PetscCall(MPIU_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
236   if (globalvcdof[0]) {
237     *sStart = vStart;
238     *sEnd   = vEnd;
239     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
240     else *ft = PETSC_VTK_POINT_FIELD;
241   } else if (globalvcdof[1]) {
242     *sStart = cStart;
243     *sEnd   = cEnd;
244     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
245     else *ft = PETSC_VTK_CELL_FIELD;
246   } else {
247     if (field >= 0) {
248       const char *fieldname;
249 
250       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
251       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
252     } else {
253       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
254     }
255   }
256   PetscFunctionReturn(PETSC_SUCCESS);
257 }
258 
259 /*@
260   DMPlexVecView1D - Plot many 1D solutions on the same line graph
261 
262   Collective
263 
264   Input Parameters:
265 + dm     - The `DMPLEX` object
266 . n      - The number of vectors
267 . u      - The array of local vectors
268 - viewer - The `PetscViewer`
269 
270   Level: advanced
271 
272 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `VecViewFromOptions()`, `VecView()`
273 @*/
274 PetscErrorCode DMPlexVecView1D(DM dm, PetscInt n, Vec u[], PetscViewer viewer)
275 {
276   PetscDS            ds;
277   PetscDraw          draw = NULL;
278   PetscDrawLG        lg;
279   Vec                coordinates;
280   const PetscScalar *coords, **sol;
281   PetscReal         *vals;
282   PetscInt          *Nc;
283   PetscInt           Nf, f, c, Nl, l, i, vStart, vEnd, v;
284   char             **names;
285 
286   PetscFunctionBegin;
287   PetscCall(DMGetDS(dm, &ds));
288   PetscCall(PetscDSGetNumFields(ds, &Nf));
289   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
290   PetscCall(PetscDSGetComponents(ds, &Nc));
291 
292   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
293   if (!draw) PetscFunctionReturn(PETSC_SUCCESS);
294   PetscCall(PetscDrawLGCreate(draw, n * Nl, &lg));
295 
296   PetscCall(PetscMalloc3(n, &sol, n * Nl, &names, n * Nl, &vals));
297   for (i = 0, l = 0; i < n; ++i) {
298     const char *vname;
299 
300     PetscCall(PetscObjectGetName((PetscObject)u[i], &vname));
301     for (f = 0; f < Nf; ++f) {
302       PetscObject disc;
303       const char *fname;
304       char        tmpname[PETSC_MAX_PATH_LEN];
305 
306       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
307       /* TODO Create names for components */
308       for (c = 0; c < Nc[f]; ++c, ++l) {
309         PetscCall(PetscObjectGetName(disc, &fname));
310         PetscCall(PetscStrncpy(tmpname, vname, sizeof(tmpname)));
311         PetscCall(PetscStrlcat(tmpname, ":", sizeof(tmpname)));
312         PetscCall(PetscStrlcat(tmpname, fname, sizeof(tmpname)));
313         PetscCall(PetscStrallocpy(tmpname, &names[l]));
314       }
315     }
316   }
317   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *)names));
318   /* Just add P_1 support for now */
319   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
320   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
321   PetscCall(VecGetArrayRead(coordinates, &coords));
322   for (i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
323   for (v = vStart; v < vEnd; ++v) {
324     PetscScalar *x, *svals;
325 
326     PetscCall(DMPlexPointLocalRead(dm, v, coords, &x));
327     for (i = 0; i < n; ++i) {
328       PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
329       for (l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
330     }
331     PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
332   }
333   PetscCall(VecRestoreArrayRead(coordinates, &coords));
334   for (i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
335   for (l = 0; l < n * Nl; ++l) PetscCall(PetscFree(names[l]));
336   PetscCall(PetscFree3(sol, names, vals));
337 
338   PetscCall(PetscDrawLGDraw(lg));
339   PetscCall(PetscDrawLGDestroy(&lg));
340   PetscFunctionReturn(PETSC_SUCCESS);
341 }
342 
343 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer)
344 {
345   DM dm;
346 
347   PetscFunctionBegin;
348   PetscCall(VecGetDM(u, &dm));
349   PetscCall(DMPlexVecView1D(dm, 1, &u, viewer));
350   PetscFunctionReturn(PETSC_SUCCESS);
351 }
352 
353 static PetscErrorCode VecView_Plex_Local_Draw_2D(Vec v, PetscViewer viewer)
354 {
355   DM                 dm;
356   PetscSection       s;
357   PetscDraw          draw, popup;
358   DM                 cdm;
359   PetscSection       coordSection;
360   Vec                coordinates;
361   const PetscScalar *array;
362   PetscReal          lbound[3], ubound[3];
363   PetscReal          vbound[2], time;
364   PetscBool          flg;
365   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
366   const char        *name;
367   char               title[PETSC_MAX_PATH_LEN];
368 
369   PetscFunctionBegin;
370   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
371   PetscCall(VecGetDM(v, &dm));
372   PetscCall(DMGetCoordinateDim(dm, &dim));
373   PetscCall(DMGetLocalSection(dm, &s));
374   PetscCall(PetscSectionGetNumFields(s, &Nf));
375   PetscCall(DMGetCoarsenLevel(dm, &level));
376   PetscCall(DMGetCoordinateDM(dm, &cdm));
377   PetscCall(DMGetLocalSection(cdm, &coordSection));
378   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
379   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
380   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
381 
382   PetscCall(PetscObjectGetName((PetscObject)v, &name));
383   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
384 
385   PetscCall(VecGetLocalSize(coordinates, &N));
386   PetscCall(DMGetBoundingBox(dm, lbound, ubound));
387   PetscCall(PetscDrawClear(draw));
388 
389   /* Could implement something like DMDASelectFields() */
390   for (f = 0; f < Nf; ++f) {
391     DM          fdm = dm;
392     Vec         fv  = v;
393     IS          fis;
394     char        prefix[PETSC_MAX_PATH_LEN];
395     const char *fname;
396 
397     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
398     PetscCall(PetscSectionGetFieldName(s, f, &fname));
399 
400     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix, sizeof(prefix)));
401     else prefix[0] = '\0';
402     if (Nf > 1) {
403       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
404       PetscCall(VecGetSubVector(v, fis, &fv));
405       PetscCall(PetscStrlcat(prefix, fname, sizeof(prefix)));
406       PetscCall(PetscStrlcat(prefix, "_", sizeof(prefix)));
407     }
408     for (comp = 0; comp < Nc; ++comp, ++w) {
409       PetscInt nmax = 2;
410 
411       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
412       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
413       else PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
414       PetscCall(PetscDrawSetTitle(draw, title));
415 
416       /* TODO Get max and min only for this component */
417       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
418       if (!flg) {
419         PetscCall(VecMin(fv, NULL, &vbound[0]));
420         PetscCall(VecMax(fv, NULL, &vbound[1]));
421         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
422       }
423 
424       PetscCall(PetscDrawGetPopup(draw, &popup));
425       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
426       PetscCall(PetscDrawSetCoordinates(draw, lbound[0], lbound[1], ubound[0], ubound[1]));
427       PetscCall(VecGetArrayRead(fv, &array));
428       for (c = cStart; c < cEnd; ++c) {
429         DMPolytopeType     ct;
430         PetscScalar       *coords = NULL, *a = NULL;
431         const PetscScalar *coords_arr;
432         PetscBool          isDG;
433         PetscInt           numCoords, color[4] = {-1, -1, -1, -1};
434 
435         PetscCall(DMPlexGetCellType(dm, c, &ct));
436         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
437         if (a) {
438           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
439           color[1] = color[2] = color[3] = color[0];
440         } else {
441           PetscScalar *vals = NULL;
442           PetscInt     numVals, va;
443 
444           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
445           if (!numVals) {
446             PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
447             continue;
448           }
449           PetscCheck(numVals % Nc == 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "The number of components %" PetscInt_FMT " does not divide the number of values in the closure %" PetscInt_FMT, Nc, numVals);
450           switch (numVals / Nc) {
451           case 1: /* P1 Clamped Segment Prism */
452           case 2: /* P1 Segment Prism, P2 Clamped Segment Prism */
453             PetscCheck(ct == DM_POLYTOPE_SEG_PRISM_TENSOR, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell should be a tensor segment, but it is a %s", DMPolytopeTypes[ct]);
454             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
455             break;
456           case 3: /* P1 Triangle */
457           case 4: /* P1 Quadrangle */
458             PetscCheck(ct == DM_POLYTOPE_TRIANGLE || ct == DM_POLYTOPE_QUADRILATERAL || ct == DM_POLYTOPE_SEG_PRISM_TENSOR, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell should be a triangle or quad, but it is a %s", DMPolytopeTypes[ct]);
459             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
460             break;
461           case 6: /* P2 Triangle */
462           case 8: /* P2 Quadrangle */
463             PetscCheck(ct == DM_POLYTOPE_TRIANGLE || ct == DM_POLYTOPE_QUADRILATERAL || ct == DM_POLYTOPE_SEG_PRISM_TENSOR, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell should be a triangle or quad, but it is a %s", DMPolytopeTypes[ct]);
464             for (va = 0; va < numVals / (Nc * 2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp + numVals / (Nc * 2)]), vbound[0], vbound[1]);
465             break;
466           default:
467             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals / Nc);
468           }
469           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
470         }
471         PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
472         switch (numCoords) {
473         case 6:
474         case 12: /* Localized triangle */
475           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
476           break;
477         case 8:
478         case 16: /* Localized quadrilateral */
479           if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR) {
480             PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscMax(color[0], color[1])));
481           } else {
482             PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
483             PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), color[2], color[3], color[0]));
484           }
485           break;
486         default:
487           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
488         }
489         PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
490       }
491       PetscCall(VecRestoreArrayRead(fv, &array));
492       PetscCall(PetscDrawFlush(draw));
493       PetscCall(PetscDrawPause(draw));
494       PetscCall(PetscDrawSave(draw));
495     }
496     if (Nf > 1) {
497       PetscCall(VecRestoreSubVector(v, fis, &fv));
498       PetscCall(ISDestroy(&fis));
499       PetscCall(DMDestroy(&fdm));
500     }
501   }
502   PetscFunctionReturn(PETSC_SUCCESS);
503 }
504 
505 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
506 {
507   DM        dm;
508   PetscDraw draw;
509   PetscInt  dim;
510   PetscBool isnull;
511 
512   PetscFunctionBegin;
513   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
514   PetscCall(PetscDrawIsNull(draw, &isnull));
515   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
516 
517   PetscCall(VecGetDM(v, &dm));
518   PetscCall(DMGetCoordinateDim(dm, &dim));
519   switch (dim) {
520   case 1:
521     PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));
522     break;
523   case 2:
524     PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));
525     break;
526   default:
527     SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
528   }
529   PetscFunctionReturn(PETSC_SUCCESS);
530 }
531 
532 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
533 {
534   DM                      dm;
535   Vec                     locv;
536   const char             *name;
537   PetscSection            section;
538   PetscInt                pStart, pEnd;
539   PetscInt                numFields;
540   PetscViewerVTKFieldType ft;
541 
542   PetscFunctionBegin;
543   PetscCall(VecGetDM(v, &dm));
544   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
545   PetscCall(PetscObjectGetName((PetscObject)v, &name));
546   PetscCall(PetscObjectSetName((PetscObject)locv, name));
547   PetscCall(VecCopy(v, locv));
548   PetscCall(DMGetLocalSection(dm, &section));
549   PetscCall(PetscSectionGetNumFields(section, &numFields));
550   if (!numFields) {
551     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
552     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE, (PetscObject)locv));
553   } else {
554     PetscInt f;
555 
556     for (f = 0; f < numFields; f++) {
557       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
558       if (ft == PETSC_VTK_INVALID) continue;
559       PetscCall(PetscObjectReference((PetscObject)locv));
560       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE, (PetscObject)locv));
561     }
562     PetscCall(VecDestroy(&locv));
563   }
564   PetscFunctionReturn(PETSC_SUCCESS);
565 }
566 
567 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
568 {
569   DM        dm;
570   PetscBool isvtk, ishdf5, isdraw, isglvis, iscgns;
571 
572   PetscFunctionBegin;
573   PetscCall(VecGetDM(v, &dm));
574   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
575   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
576   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
577   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
578   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
579   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
580   if (isvtk || ishdf5 || isdraw || isglvis || iscgns) {
581     PetscInt    i, numFields;
582     PetscObject fe;
583     PetscBool   fem  = PETSC_FALSE;
584     Vec         locv = v;
585     const char *name;
586     PetscInt    step;
587     PetscReal   time;
588 
589     PetscCall(DMGetNumFields(dm, &numFields));
590     for (i = 0; i < numFields; i++) {
591       PetscCall(DMGetField(dm, i, NULL, &fe));
592       if (fe->classid == PETSCFE_CLASSID) {
593         fem = PETSC_TRUE;
594         break;
595       }
596     }
597     if (fem) {
598       PetscObject isZero;
599 
600       PetscCall(DMGetLocalVector(dm, &locv));
601       PetscCall(PetscObjectGetName((PetscObject)v, &name));
602       PetscCall(PetscObjectSetName((PetscObject)locv, name));
603       PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
604       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
605       PetscCall(VecCopy(v, locv));
606       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
607       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
608     }
609     if (isvtk) {
610       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
611     } else if (ishdf5) {
612 #if defined(PETSC_HAVE_HDF5)
613       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
614 #else
615       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
616 #endif
617     } else if (isdraw) {
618       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
619     } else if (isglvis) {
620       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
621       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
622       PetscCall(VecView_GLVis(locv, viewer));
623     } else if (iscgns) {
624 #if defined(PETSC_HAVE_CGNS)
625       PetscCall(VecView_Plex_Local_CGNS(locv, viewer));
626 #else
627       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
628 #endif
629     }
630     if (fem) {
631       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
632       PetscCall(DMRestoreLocalVector(dm, &locv));
633     }
634   } else {
635     PetscBool isseq;
636 
637     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
638     if (isseq) PetscCall(VecView_Seq(v, viewer));
639     else PetscCall(VecView_MPI(v, viewer));
640   }
641   PetscFunctionReturn(PETSC_SUCCESS);
642 }
643 
644 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
645 {
646   DM        dm;
647   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii, iscgns;
648 
649   PetscFunctionBegin;
650   PetscCall(VecGetDM(v, &dm));
651   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
652   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
653   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
654   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
655   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
656   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
657   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
658   if (isvtk || isdraw || isglvis || iscgns) {
659     Vec         locv;
660     PetscObject isZero;
661     const char *name;
662 
663     PetscCall(DMGetLocalVector(dm, &locv));
664     PetscCall(PetscObjectGetName((PetscObject)v, &name));
665     PetscCall(PetscObjectSetName((PetscObject)locv, name));
666     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
667     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
668     PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
669     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
670     PetscCall(VecView_Plex_Local(locv, viewer));
671     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
672     PetscCall(DMRestoreLocalVector(dm, &locv));
673     /* Call flush for proper logging of VecView timings */
674     if (isvtk) PetscCall(PetscViewerFlush(viewer));
675   } else if (ishdf5) {
676 #if defined(PETSC_HAVE_HDF5)
677     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
678 #else
679     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
680 #endif
681   } else if (isexodusii) {
682 #if defined(PETSC_HAVE_EXODUSII)
683     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
684 #else
685     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
686 #endif
687   } else {
688     PetscBool isseq;
689 
690     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
691     if (isseq) PetscCall(VecView_Seq(v, viewer));
692     else PetscCall(VecView_MPI(v, viewer));
693   }
694   PetscFunctionReturn(PETSC_SUCCESS);
695 }
696 
697 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
698 {
699   DM                dm;
700   MPI_Comm          comm;
701   PetscViewerFormat format;
702   Vec               v;
703   PetscBool         isvtk, ishdf5;
704 
705   PetscFunctionBegin;
706   PetscCall(VecGetDM(originalv, &dm));
707   PetscCall(PetscObjectGetComm((PetscObject)originalv, &comm));
708   PetscCheck(dm, comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
709   PetscCall(PetscViewerGetFormat(viewer, &format));
710   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
711   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
712   if (format == PETSC_VIEWER_NATIVE) {
713     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
714     /* this need a better fix */
715     if (dm->useNatural) {
716       if (dm->sfNatural) {
717         const char *vecname;
718         PetscInt    n, nroots;
719 
720         PetscCall(VecGetLocalSize(originalv, &n));
721         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
722         if (n == nroots) {
723           PetscCall(DMPlexCreateNaturalVector(dm, &v));
724           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
725           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
726           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
727           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
728         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
729       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
730     } else v = originalv;
731   } else v = originalv;
732 
733   if (ishdf5) {
734 #if defined(PETSC_HAVE_HDF5)
735     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
736 #else
737     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
738 #endif
739   } else if (isvtk) {
740     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
741   } else {
742     PetscBool isseq;
743 
744     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
745     if (isseq) PetscCall(VecView_Seq(v, viewer));
746     else PetscCall(VecView_MPI(v, viewer));
747   }
748   if (v != originalv) PetscCall(VecDestroy(&v));
749   PetscFunctionReturn(PETSC_SUCCESS);
750 }
751 
752 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
753 {
754   DM        dm;
755   PetscBool ishdf5;
756 
757   PetscFunctionBegin;
758   PetscCall(VecGetDM(v, &dm));
759   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
760   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
761   if (ishdf5) {
762     DM          dmBC;
763     Vec         gv;
764     const char *name;
765 
766     PetscCall(DMGetOutputDM(dm, &dmBC));
767     PetscCall(DMGetGlobalVector(dmBC, &gv));
768     PetscCall(PetscObjectGetName((PetscObject)v, &name));
769     PetscCall(PetscObjectSetName((PetscObject)gv, name));
770     PetscCall(VecLoad_Default(gv, viewer));
771     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
772     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
773     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
774   } else PetscCall(VecLoad_Default(v, viewer));
775   PetscFunctionReturn(PETSC_SUCCESS);
776 }
777 
778 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
779 {
780   DM        dm;
781   PetscBool ishdf5, isexodusii, iscgns;
782 
783   PetscFunctionBegin;
784   PetscCall(VecGetDM(v, &dm));
785   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
786   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
787   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
788   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
789   if (ishdf5) {
790 #if defined(PETSC_HAVE_HDF5)
791     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
792 #else
793     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
794 #endif
795   } else if (isexodusii) {
796 #if defined(PETSC_HAVE_EXODUSII)
797     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
798 #else
799     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
800 #endif
801   } else if (iscgns) {
802 #if defined(PETSC_HAVE_CGNS)
803     PetscCall(VecLoad_Plex_CGNS_Internal(v, viewer));
804 #else
805     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
806 #endif
807   } else PetscCall(VecLoad_Default(v, viewer));
808   PetscFunctionReturn(PETSC_SUCCESS);
809 }
810 
811 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
812 {
813   DM                dm;
814   PetscViewerFormat format;
815   PetscBool         ishdf5;
816 
817   PetscFunctionBegin;
818   PetscCall(VecGetDM(originalv, &dm));
819   PetscCheck(dm, PetscObjectComm((PetscObject)originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
820   PetscCall(PetscViewerGetFormat(viewer, &format));
821   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
822   if (format == PETSC_VIEWER_NATIVE) {
823     if (dm->useNatural) {
824       if (dm->sfNatural) {
825         if (ishdf5) {
826 #if defined(PETSC_HAVE_HDF5)
827           Vec         v;
828           const char *vecname;
829 
830           PetscCall(DMPlexCreateNaturalVector(dm, &v));
831           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
832           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
833           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
834           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
835           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
836           PetscCall(VecDestroy(&v));
837 #else
838           SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
839 #endif
840         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
841       }
842     } else PetscCall(VecLoad_Default(originalv, viewer));
843   }
844   PetscFunctionReturn(PETSC_SUCCESS);
845 }
846 
847 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
848 {
849   PetscSection       coordSection;
850   Vec                coordinates;
851   DMLabel            depthLabel, celltypeLabel;
852   const char        *name[4];
853   const PetscScalar *a;
854   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
855 
856   PetscFunctionBegin;
857   PetscCall(DMGetDimension(dm, &dim));
858   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
859   PetscCall(DMGetCoordinateSection(dm, &coordSection));
860   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
861   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
862   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
863   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
864   PetscCall(VecGetArrayRead(coordinates, &a));
865   name[0]       = "vertex";
866   name[1]       = "edge";
867   name[dim - 1] = "face";
868   name[dim]     = "cell";
869   for (c = cStart; c < cEnd; ++c) {
870     PetscInt *closure = NULL;
871     PetscInt  closureSize, cl, ct;
872 
873     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
874     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
875     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
876     PetscCall(PetscViewerASCIIPushTab(viewer));
877     for (cl = 0; cl < closureSize * 2; cl += 2) {
878       PetscInt point = closure[cl], depth, dof, off, d, p;
879 
880       if ((point < pStart) || (point >= pEnd)) continue;
881       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
882       if (!dof) continue;
883       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
884       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
885       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
886       for (p = 0; p < dof / dim; ++p) {
887         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
888         for (d = 0; d < dim; ++d) {
889           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
890           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double)PetscRealPart(a[off + p * dim + d])));
891         }
892         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
893       }
894       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
895     }
896     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
897     PetscCall(PetscViewerASCIIPopTab(viewer));
898   }
899   PetscCall(VecRestoreArrayRead(coordinates, &a));
900   PetscFunctionReturn(PETSC_SUCCESS);
901 }
902 
903 typedef enum {
904   CS_CARTESIAN,
905   CS_POLAR,
906   CS_CYLINDRICAL,
907   CS_SPHERICAL
908 } CoordSystem;
909 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
910 
911 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
912 {
913   PetscInt i;
914 
915   PetscFunctionBegin;
916   if (dim > 3) {
917     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)PetscRealPart(x[i])));
918   } else {
919     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
920 
921     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
922     switch (cs) {
923     case CS_CARTESIAN:
924       for (i = 0; i < dim; ++i) trcoords[i] = coords[i];
925       break;
926     case CS_POLAR:
927       PetscCheck(dim == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
928       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
929       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
930       break;
931     case CS_CYLINDRICAL:
932       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
933       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
934       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
935       trcoords[2] = coords[2];
936       break;
937     case CS_SPHERICAL:
938       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
939       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
940       trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
941       trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
942       break;
943     }
944     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)trcoords[i]));
945   }
946   PetscFunctionReturn(PETSC_SUCCESS);
947 }
948 
949 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
950 {
951   DM_Plex          *mesh = (DM_Plex *)dm->data;
952   DM                cdm, cdmCell;
953   PetscSection      coordSection, coordSectionCell;
954   Vec               coordinates, coordinatesCell;
955   PetscViewerFormat format;
956 
957   PetscFunctionBegin;
958   PetscCall(PetscViewerGetFormat(viewer, &format));
959   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
960     const char *name;
961     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
962     PetscInt    pStart, pEnd, p, numLabels, l;
963     PetscMPIInt rank, size;
964 
965     PetscCall(DMGetCoordinateDM(dm, &cdm));
966     PetscCall(DMGetCoordinateSection(dm, &coordSection));
967     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
968     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
969     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
970     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
971     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
972     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
973     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
974     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
975     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
976     PetscCall(DMGetDimension(dm, &dim));
977     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
978     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
979     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
980     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
981     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
982     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
983     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
984     for (p = pStart; p < pEnd; ++p) {
985       PetscInt dof, off, s;
986 
987       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
988       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
989       for (s = off; s < off + dof; ++s) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
990     }
991     PetscCall(PetscViewerFlush(viewer));
992     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
993     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
994     for (p = pStart; p < pEnd; ++p) {
995       PetscInt dof, off, c;
996 
997       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
998       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
999       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]));
1000     }
1001     PetscCall(PetscViewerFlush(viewer));
1002     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1003     if (coordSection && coordinates) {
1004       CoordSystem        cs = CS_CARTESIAN;
1005       const PetscScalar *array, *arrayCell = NULL;
1006       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_MAX_INT, pcEnd = PETSC_MIN_INT, pStart, pEnd, p;
1007       PetscMPIInt        rank;
1008       const char        *name;
1009 
1010       PetscCall(PetscOptionsGetEnum(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *)&cs, NULL));
1011       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
1012       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
1013       PetscCheck(Nf == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
1014       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
1015       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
1016       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
1017       pStart = PetscMin(pvStart, pcStart);
1018       pEnd   = PetscMax(pvEnd, pcEnd);
1019       PetscCall(PetscObjectGetName((PetscObject)coordinates, &name));
1020       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
1021       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
1022       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
1023 
1024       PetscCall(VecGetArrayRead(coordinates, &array));
1025       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
1026       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1027       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
1028       for (p = pStart; p < pEnd; ++p) {
1029         PetscInt dof, off;
1030 
1031         if (p >= pvStart && p < pvEnd) {
1032           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
1033           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
1034           if (dof) {
1035             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1036             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
1037             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1038           }
1039         }
1040         if (cdmCell && p >= pcStart && p < pcEnd) {
1041           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
1042           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
1043           if (dof) {
1044             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1045             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
1046             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1047           }
1048         }
1049       }
1050       PetscCall(PetscViewerFlush(viewer));
1051       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1052       PetscCall(VecRestoreArrayRead(coordinates, &array));
1053       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
1054     }
1055     PetscCall(DMGetNumLabels(dm, &numLabels));
1056     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1057     for (l = 0; l < numLabels; ++l) {
1058       DMLabel     label;
1059       PetscBool   isdepth;
1060       const char *name;
1061 
1062       PetscCall(DMGetLabelName(dm, l, &name));
1063       PetscCall(PetscStrcmp(name, "depth", &isdepth));
1064       if (isdepth) continue;
1065       PetscCall(DMGetLabel(dm, name, &label));
1066       PetscCall(DMLabelView(label, viewer));
1067     }
1068     if (size > 1) {
1069       PetscSF sf;
1070 
1071       PetscCall(DMGetPointSF(dm, &sf));
1072       PetscCall(PetscSFView(sf, viewer));
1073     }
1074     if (mesh->periodic.face_sfs)
1075       for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(PetscSFView(mesh->periodic.face_sfs[i], viewer));
1076     PetscCall(PetscViewerFlush(viewer));
1077   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
1078     const char  *name, *color;
1079     const char  *defcolors[3]  = {"gray", "orange", "green"};
1080     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
1081     char         lname[PETSC_MAX_PATH_LEN];
1082     PetscReal    scale      = 2.0;
1083     PetscReal    tikzscale  = 1.0;
1084     PetscBool    useNumbers = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
1085     double       tcoords[3];
1086     PetscScalar *coords;
1087     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;
1088     PetscMPIInt  rank, size;
1089     char       **names, **colors, **lcolors;
1090     PetscBool    flg, lflg;
1091     PetscBT      wp = NULL;
1092     PetscInt     pEnd, pStart;
1093 
1094     PetscCall(DMGetCoordinateDM(dm, &cdm));
1095     PetscCall(DMGetCoordinateSection(dm, &coordSection));
1096     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1097     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
1098     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
1099     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
1100     PetscCall(DMGetDimension(dm, &dim));
1101     PetscCall(DMPlexGetDepth(dm, &depth));
1102     PetscCall(DMGetNumLabels(dm, &numLabels));
1103     numLabels  = PetscMax(numLabels, 10);
1104     numColors  = 10;
1105     numLColors = 10;
1106     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
1107     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
1108     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
1109     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
1110     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
1111     for (d = 0; d < 4; ++d) drawColors[d] = PETSC_TRUE;
1112     n = 4;
1113     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
1114     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1115     n = 4;
1116     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
1117     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1118     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
1119     if (!useLabels) numLabels = 0;
1120     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
1121     if (!useColors) {
1122       numColors = 3;
1123       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
1124     }
1125     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
1126     if (!useColors) {
1127       numLColors = 4;
1128       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
1129     }
1130     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
1131     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
1132     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
1133     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
1134     if (depth < dim) plotEdges = PETSC_FALSE;
1135     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
1136 
1137     /* filter points with labelvalue != labeldefaultvalue */
1138     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1139     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1140     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
1141     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1142     PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
1143     if (lflg) {
1144       DMLabel lbl;
1145 
1146       PetscCall(DMGetLabel(dm, lname, &lbl));
1147       if (lbl) {
1148         PetscInt val, defval;
1149 
1150         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
1151         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
1152         for (c = pStart; c < pEnd; c++) {
1153           PetscInt *closure = NULL;
1154           PetscInt  closureSize;
1155 
1156           PetscCall(DMLabelGetValue(lbl, c, &val));
1157           if (val == defval) continue;
1158 
1159           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1160           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
1161           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1162         }
1163       }
1164     }
1165 
1166     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1167     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1168     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1169     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1170 \\documentclass[tikz]{standalone}\n\n\
1171 \\usepackage{pgflibraryshapes}\n\
1172 \\usetikzlibrary{backgrounds}\n\
1173 \\usetikzlibrary{arrows}\n\
1174 \\begin{document}\n"));
1175     if (size > 1) {
1176       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1177       for (p = 0; p < size; ++p) {
1178         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1179         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1180       }
1181       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1182     }
1183     if (drawHasse) {
1184       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, PetscMax(fEnd - fStart, cEnd - cStart)));
1185 
1186       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1187       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1188       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1189       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1190       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1191       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1192       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1193       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1194       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fStart}{%" PetscInt_FMT "}\n", fStart));
1195       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fEnd}{%" PetscInt_FMT "}\n", fEnd - 1));
1196       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fShift}{%.2f}\n", 3 + (maxStratum - (fEnd - fStart)) / 2.));
1197       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numFaces}{%" PetscInt_FMT "}\n", fEnd - fStart));
1198       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1199       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1200       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1201       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1202     }
1203     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1204 
1205     /* Plot vertices */
1206     PetscCall(VecGetArray(coordinates, &coords));
1207     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1208     for (v = vStart; v < vEnd; ++v) {
1209       PetscInt  off, dof, d;
1210       PetscBool isLabeled = PETSC_FALSE;
1211 
1212       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1213       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1214       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1215       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1216       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1217       for (d = 0; d < dof; ++d) {
1218         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1219         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1220       }
1221       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1222       if (dim == 3) {
1223         PetscReal tmp = tcoords[1];
1224         tcoords[1]    = tcoords[2];
1225         tcoords[2]    = -tmp;
1226       }
1227       for (d = 0; d < dof; ++d) {
1228         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1229         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1230       }
1231       if (drawHasse) color = colors[0 % numColors];
1232       else color = colors[rank % numColors];
1233       for (l = 0; l < numLabels; ++l) {
1234         PetscInt val;
1235         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1236         if (val >= 0) {
1237           color     = lcolors[l % numLColors];
1238           isLabeled = PETSC_TRUE;
1239           break;
1240         }
1241       }
1242       if (drawNumbers[0]) {
1243         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1244       } else if (drawColors[0]) {
1245         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1246       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1247     }
1248     PetscCall(VecRestoreArray(coordinates, &coords));
1249     PetscCall(PetscViewerFlush(viewer));
1250     /* Plot edges */
1251     if (plotEdges) {
1252       PetscCall(VecGetArray(coordinates, &coords));
1253       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1254       for (e = eStart; e < eEnd; ++e) {
1255         const PetscInt *cone;
1256         PetscInt        coneSize, offA, offB, dof, d;
1257 
1258         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1259         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1260         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1261         PetscCall(DMPlexGetCone(dm, e, &cone));
1262         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1263         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1264         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1265         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1266         for (d = 0; d < dof; ++d) {
1267           tcoords[d] = (double)(0.5 * scale * PetscRealPart(coords[offA + d] + coords[offB + d]));
1268           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1269         }
1270         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1271         if (dim == 3) {
1272           PetscReal tmp = tcoords[1];
1273           tcoords[1]    = tcoords[2];
1274           tcoords[2]    = -tmp;
1275         }
1276         for (d = 0; d < dof; ++d) {
1277           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1278           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1279         }
1280         if (drawHasse) color = colors[1 % numColors];
1281         else color = colors[rank % numColors];
1282         for (l = 0; l < numLabels; ++l) {
1283           PetscInt val;
1284           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1285           if (val >= 0) {
1286             color = lcolors[l % numLColors];
1287             break;
1288           }
1289         }
1290         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1291       }
1292       PetscCall(VecRestoreArray(coordinates, &coords));
1293       PetscCall(PetscViewerFlush(viewer));
1294       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1295     }
1296     /* Plot cells */
1297     if (dim == 3 || !drawNumbers[1]) {
1298       for (e = eStart; e < eEnd; ++e) {
1299         const PetscInt *cone;
1300 
1301         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1302         color = colors[rank % numColors];
1303         for (l = 0; l < numLabels; ++l) {
1304           PetscInt val;
1305           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1306           if (val >= 0) {
1307             color = lcolors[l % numLColors];
1308             break;
1309           }
1310         }
1311         PetscCall(DMPlexGetCone(dm, e, &cone));
1312         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1313       }
1314     } else {
1315       DMPolytopeType ct;
1316 
1317       /* Drawing a 2D polygon */
1318       for (c = cStart; c < cEnd; ++c) {
1319         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1320         PetscCall(DMPlexGetCellType(dm, c, &ct));
1321         if (DMPolytopeTypeIsHybrid(ct)) {
1322           const PetscInt *cone;
1323           PetscInt        coneSize, e;
1324 
1325           PetscCall(DMPlexGetCone(dm, c, &cone));
1326           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1327           for (e = 0; e < coneSize; ++e) {
1328             const PetscInt *econe;
1329 
1330             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1331             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));
1332           }
1333         } else {
1334           PetscInt *closure = NULL;
1335           PetscInt  closureSize, Nv = 0, v;
1336 
1337           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1338           for (p = 0; p < closureSize * 2; p += 2) {
1339             const PetscInt point = closure[p];
1340 
1341             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1342           }
1343           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1344           for (v = 0; v <= Nv; ++v) {
1345             const PetscInt vertex = closure[v % Nv];
1346 
1347             if (v > 0) {
1348               if (plotEdges) {
1349                 const PetscInt *edge;
1350                 PetscInt        endpoints[2], ne;
1351 
1352                 endpoints[0] = closure[v - 1];
1353                 endpoints[1] = vertex;
1354                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1355                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1356                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1357                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1358               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1359             }
1360             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1361           }
1362           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1363           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1364         }
1365       }
1366     }
1367     for (c = cStart; c < cEnd; ++c) {
1368       double             ccoords[3] = {0.0, 0.0, 0.0};
1369       PetscBool          isLabeled  = PETSC_FALSE;
1370       PetscScalar       *cellCoords = NULL;
1371       const PetscScalar *array;
1372       PetscInt           numCoords, cdim, d;
1373       PetscBool          isDG;
1374 
1375       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1376       PetscCall(DMGetCoordinateDim(dm, &cdim));
1377       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1378       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1379       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1380       for (p = 0; p < numCoords / cdim; ++p) {
1381         for (d = 0; d < cdim; ++d) {
1382           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1383           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1384         }
1385         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1386         if (cdim == 3) {
1387           PetscReal tmp = tcoords[1];
1388           tcoords[1]    = tcoords[2];
1389           tcoords[2]    = -tmp;
1390         }
1391         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1392       }
1393       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1394       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1395       for (d = 0; d < cdim; ++d) {
1396         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1397         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)ccoords[d]));
1398       }
1399       if (drawHasse) color = colors[depth % numColors];
1400       else color = colors[rank % numColors];
1401       for (l = 0; l < numLabels; ++l) {
1402         PetscInt val;
1403         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1404         if (val >= 0) {
1405           color     = lcolors[l % numLColors];
1406           isLabeled = PETSC_TRUE;
1407           break;
1408         }
1409       }
1410       if (drawNumbers[dim]) {
1411         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1412       } else if (drawColors[dim]) {
1413         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1414       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1415     }
1416     if (drawHasse) {
1417       int height = 0;
1418 
1419       color = colors[depth % numColors];
1420       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1421       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1422       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1423       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,%d) {\\c};\n", rank, color, height++));
1424       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1425 
1426       if (depth > 2) {
1427         color = colors[1 % numColors];
1428         PetscCall(PetscViewerASCIIPrintf(viewer, "%% Faces\n"));
1429         PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\f in {\\fStart,...,\\fEnd}\n"));
1430         PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1431         PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\f_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\fShift+\\f-\\fStart,%d) {\\f};\n", rank, color, height++));
1432         PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1433       }
1434 
1435       color = colors[1 % numColors];
1436       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1437       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1438       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1439       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,%d) {\\e};\n", rank, color, height++));
1440       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1441 
1442       color = colors[0 % numColors];
1443       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1444       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1445       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1446       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,%d) {\\v};\n", rank, color, height++));
1447       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1448 
1449       for (p = pStart; p < pEnd; ++p) {
1450         const PetscInt *cone;
1451         PetscInt        coneSize, cp;
1452 
1453         PetscCall(DMPlexGetCone(dm, p, &cone));
1454         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1455         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1456       }
1457     }
1458     PetscCall(PetscViewerFlush(viewer));
1459     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1460     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1461     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1462     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1463     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1464     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1465     PetscCall(PetscFree3(names, colors, lcolors));
1466     PetscCall(PetscBTDestroy(&wp));
1467   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1468     Vec                    cown, acown;
1469     VecScatter             sct;
1470     ISLocalToGlobalMapping g2l;
1471     IS                     gid, acis;
1472     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1473     MPI_Group              ggroup, ngroup;
1474     PetscScalar           *array, nid;
1475     const PetscInt        *idxs;
1476     PetscInt              *idxs2, *start, *adjacency, *work;
1477     PetscInt64             lm[3], gm[3];
1478     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1479     PetscMPIInt            d1, d2, rank;
1480 
1481     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1482     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1483 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1484     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1485 #endif
1486     if (ncomm != MPI_COMM_NULL) {
1487       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1488       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1489       d1 = 0;
1490       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1491       nid = d2;
1492       PetscCallMPI(MPI_Group_free(&ggroup));
1493       PetscCallMPI(MPI_Group_free(&ngroup));
1494       PetscCallMPI(MPI_Comm_free(&ncomm));
1495     } else nid = 0.0;
1496 
1497     /* Get connectivity */
1498     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1499     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1500 
1501     /* filter overlapped local cells */
1502     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1503     PetscCall(ISGetIndices(gid, &idxs));
1504     PetscCall(ISGetLocalSize(gid, &cum));
1505     PetscCall(PetscMalloc1(cum, &idxs2));
1506     for (c = cStart, cum = 0; c < cEnd; c++) {
1507       if (idxs[c - cStart] < 0) continue;
1508       idxs2[cum++] = idxs[c - cStart];
1509     }
1510     PetscCall(ISRestoreIndices(gid, &idxs));
1511     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1512     PetscCall(ISDestroy(&gid));
1513     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1514 
1515     /* support for node-aware cell locality */
1516     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1517     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1518     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1519     PetscCall(VecGetArray(cown, &array));
1520     for (c = 0; c < numVertices; c++) array[c] = nid;
1521     PetscCall(VecRestoreArray(cown, &array));
1522     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1523     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1524     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1525     PetscCall(ISDestroy(&acis));
1526     PetscCall(VecScatterDestroy(&sct));
1527     PetscCall(VecDestroy(&cown));
1528 
1529     /* compute edgeCut */
1530     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1531     PetscCall(PetscMalloc1(cum, &work));
1532     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1533     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1534     PetscCall(ISDestroy(&gid));
1535     PetscCall(VecGetArray(acown, &array));
1536     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1537       PetscInt totl;
1538 
1539       totl = start[c + 1] - start[c];
1540       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1541       for (i = 0; i < totl; i++) {
1542         if (work[i] < 0) {
1543           ect += 1;
1544           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1545         }
1546       }
1547     }
1548     PetscCall(PetscFree(work));
1549     PetscCall(VecRestoreArray(acown, &array));
1550     lm[0] = numVertices > 0 ? numVertices : PETSC_MAX_INT;
1551     lm[1] = -numVertices;
1552     PetscCall(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1553     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT, -((double)gm[1]) / ((double)gm[0]), -(PetscInt)gm[1], (PetscInt)gm[0]));
1554     lm[0] = ect;                     /* edgeCut */
1555     lm[1] = ectn;                    /* node-aware edgeCut */
1556     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1557     PetscCall(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1558     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt_FMT ")\n", (PetscInt)gm[2]));
1559 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1560     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), gm[0] ? ((double)gm[1]) / ((double)gm[0]) : 1.));
1561 #else
1562     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), 0.0));
1563 #endif
1564     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1565     PetscCall(PetscFree(start));
1566     PetscCall(PetscFree(adjacency));
1567     PetscCall(VecDestroy(&acown));
1568   } else {
1569     const char    *name;
1570     PetscInt      *sizes, *hybsizes, *ghostsizes;
1571     PetscInt       locDepth, depth, cellHeight, dim, d;
1572     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1573     PetscInt       numLabels, l, maxSize = 17;
1574     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1575     MPI_Comm       comm;
1576     PetscMPIInt    size, rank;
1577 
1578     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1579     PetscCallMPI(MPI_Comm_size(comm, &size));
1580     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1581     PetscCall(DMGetDimension(dm, &dim));
1582     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1583     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1584     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1585     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1586     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1587     PetscCall(DMPlexGetDepth(dm, &locDepth));
1588     PetscCall(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1589     PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &gcStart, &gcEnd));
1590     gcNum = gcEnd - gcStart;
1591     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1592     else PetscCall(PetscCalloc3(3, &sizes, 3, &hybsizes, 3, &ghostsizes));
1593     for (d = 0; d <= depth; d++) {
1594       PetscInt Nc[2] = {0, 0}, ict;
1595 
1596       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1597       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1598       ict = ct0;
1599       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1600       ct0 = (DMPolytopeType)ict;
1601       for (p = pStart; p < pEnd; ++p) {
1602         DMPolytopeType ct;
1603 
1604         PetscCall(DMPlexGetCellType(dm, p, &ct));
1605         if (ct == ct0) ++Nc[0];
1606         else ++Nc[1];
1607       }
1608       if (size < maxSize) {
1609         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes, 1, MPIU_INT, 0, comm));
1610         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1611         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1612         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1613         for (p = 0; p < size; ++p) {
1614           if (rank == 0) {
1615             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p] + hybsizes[p]));
1616             if (hybsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1617             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1618           }
1619         }
1620       } else {
1621         PetscInt locMinMax[2];
1622 
1623         locMinMax[0] = Nc[0] + Nc[1];
1624         locMinMax[1] = Nc[0] + Nc[1];
1625         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1626         locMinMax[0] = Nc[1];
1627         locMinMax[1] = Nc[1];
1628         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1629         if (d == depth) {
1630           locMinMax[0] = gcNum;
1631           locMinMax[1] = gcNum;
1632           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1633         }
1634         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1635         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1636         if (hybsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1637         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1638       }
1639       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1640     }
1641     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1642     {
1643       const PetscReal *maxCell;
1644       const PetscReal *L;
1645       PetscBool        localized;
1646 
1647       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1648       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1649       if (L || localized) {
1650         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1651         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1652         if (L) {
1653           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1654           for (d = 0; d < dim; ++d) {
1655             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1656             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1657           }
1658           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1659         }
1660         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1661         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1662       }
1663     }
1664     PetscCall(DMGetNumLabels(dm, &numLabels));
1665     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1666     for (l = 0; l < numLabels; ++l) {
1667       DMLabel     label;
1668       const char *name;
1669       PetscInt   *values;
1670       PetscInt    numValues, v;
1671 
1672       PetscCall(DMGetLabelName(dm, l, &name));
1673       PetscCall(DMGetLabel(dm, name, &label));
1674       PetscCall(DMLabelGetNumValues(label, &numValues));
1675       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1676 
1677       { // Extract array of DMLabel values so it can be sorted
1678         IS              is_values;
1679         const PetscInt *is_values_local = NULL;
1680 
1681         PetscCall(DMLabelGetValueIS(label, &is_values));
1682         PetscCall(ISGetIndices(is_values, &is_values_local));
1683         PetscCall(PetscMalloc1(numValues, &values));
1684         PetscCall(PetscArraycpy(values, is_values_local, numValues));
1685         PetscCall(PetscSortInt(numValues, values));
1686         PetscCall(ISRestoreIndices(is_values, &is_values_local));
1687         PetscCall(ISDestroy(&is_values));
1688       }
1689       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1690       for (v = 0; v < numValues; ++v) {
1691         PetscInt size;
1692 
1693         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1694         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1695         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1696       }
1697       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1698       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1699       PetscCall(PetscFree(values));
1700     }
1701     {
1702       char    **labelNames;
1703       PetscInt  Nl = numLabels;
1704       PetscBool flg;
1705 
1706       PetscCall(PetscMalloc1(Nl, &labelNames));
1707       PetscCall(PetscOptionsGetStringArray(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1708       for (l = 0; l < Nl; ++l) {
1709         DMLabel label;
1710 
1711         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1712         if (flg) {
1713           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1714           PetscCall(DMLabelView(label, viewer));
1715         }
1716         PetscCall(PetscFree(labelNames[l]));
1717       }
1718       PetscCall(PetscFree(labelNames));
1719     }
1720     /* If no fields are specified, people do not want to see adjacency */
1721     if (dm->Nf) {
1722       PetscInt f;
1723 
1724       for (f = 0; f < dm->Nf; ++f) {
1725         const char *name;
1726 
1727         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1728         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1729         PetscCall(PetscViewerASCIIPushTab(viewer));
1730         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1731         if (dm->fields[f].adjacency[0]) {
1732           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1733           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1734         } else {
1735           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1736           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1737         }
1738         PetscCall(PetscViewerASCIIPopTab(viewer));
1739       }
1740     }
1741     PetscCall(DMGetCoarseDM(dm, &cdm));
1742     if (cdm) {
1743       PetscCall(PetscViewerASCIIPushTab(viewer));
1744       PetscCall(PetscViewerASCIIPrintf(viewer, "Defined by transform from:\n"));
1745       PetscCall(DMPlexView_Ascii(cdm, viewer));
1746       PetscCall(PetscViewerASCIIPopTab(viewer));
1747     }
1748   }
1749   PetscFunctionReturn(PETSC_SUCCESS);
1750 }
1751 
1752 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1753 {
1754   DMPolytopeType ct;
1755   PetscMPIInt    rank;
1756   PetscInt       cdim;
1757 
1758   PetscFunctionBegin;
1759   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1760   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1761   PetscCall(DMGetCoordinateDim(dm, &cdim));
1762   switch (ct) {
1763   case DM_POLYTOPE_SEGMENT:
1764   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1765     switch (cdim) {
1766     case 1: {
1767       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1768       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1769 
1770       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, PETSC_DRAW_BLACK));
1771       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, PETSC_DRAW_BLACK));
1772       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, PETSC_DRAW_BLACK));
1773     } break;
1774     case 2: {
1775       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1776       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1777       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1778 
1779       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1780       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));
1781       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));
1782     } break;
1783     default:
1784       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1785     }
1786     break;
1787   case DM_POLYTOPE_TRIANGLE:
1788     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));
1789     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1790     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1791     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1792     break;
1793   case DM_POLYTOPE_QUADRILATERAL:
1794     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));
1795     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));
1796     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1797     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1798     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1799     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1800     break;
1801   case DM_POLYTOPE_SEG_PRISM_TENSOR:
1802     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));
1803     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));
1804     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1805     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1806     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1807     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1808     break;
1809   case DM_POLYTOPE_FV_GHOST:
1810     break;
1811   default:
1812     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1813   }
1814   PetscFunctionReturn(PETSC_SUCCESS);
1815 }
1816 
1817 static PetscErrorCode DrawPolygon_Private(DM dm, PetscDraw draw, PetscInt cell, PetscInt Nv, const PetscReal refVertices[], const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1818 {
1819   PetscReal   centroid[2] = {0., 0.};
1820   PetscMPIInt rank;
1821   PetscInt    fillColor;
1822 
1823   PetscFunctionBegin;
1824   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1825   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1826   for (PetscInt v = 0; v < Nv; ++v) {
1827     centroid[0] += PetscRealPart(coords[v * 2 + 0]) / Nv;
1828     centroid[1] += PetscRealPart(coords[v * 2 + 1]) / Nv;
1829   }
1830   for (PetscInt e = 0; e < Nv; ++e) {
1831     refCoords[0] = refVertices[e * 2 + 0];
1832     refCoords[1] = refVertices[e * 2 + 1];
1833     for (PetscInt d = 1; d <= edgeDiv; ++d) {
1834       refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % Nv * 2 + 0] - refCoords[0]) * d / edgeDiv;
1835       refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % Nv * 2 + 1] - refCoords[1]) * d / edgeDiv;
1836     }
1837     PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1838     for (PetscInt d = 0; d < edgeDiv; ++d) {
1839       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));
1840       PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1841     }
1842   }
1843   PetscFunctionReturn(PETSC_SUCCESS);
1844 }
1845 
1846 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1847 {
1848   DMPolytopeType ct;
1849 
1850   PetscFunctionBegin;
1851   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1852   switch (ct) {
1853   case DM_POLYTOPE_TRIANGLE: {
1854     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1855 
1856     PetscCall(DrawPolygon_Private(dm, draw, cell, 3, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1857   } break;
1858   case DM_POLYTOPE_QUADRILATERAL: {
1859     PetscReal refVertices[8] = {-1., -1., 1., -1., 1., 1., -1., 1.};
1860 
1861     PetscCall(DrawPolygon_Private(dm, draw, cell, 4, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1862   } break;
1863   default:
1864     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1865   }
1866   PetscFunctionReturn(PETSC_SUCCESS);
1867 }
1868 
1869 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1870 {
1871   PetscDraw    draw;
1872   DM           cdm;
1873   PetscSection coordSection;
1874   Vec          coordinates;
1875   PetscReal    xyl[3], xyr[3];
1876   PetscReal   *refCoords, *edgeCoords;
1877   PetscBool    isnull, drawAffine;
1878   PetscInt     dim, vStart, vEnd, cStart, cEnd, c, cDegree, edgeDiv;
1879 
1880   PetscFunctionBegin;
1881   PetscCall(DMGetCoordinateDim(dm, &dim));
1882   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1883   PetscCall(DMGetCoordinateDegree_Internal(dm, &cDegree));
1884   drawAffine = cDegree > 1 ? PETSC_FALSE : PETSC_TRUE;
1885   edgeDiv    = cDegree + 1;
1886   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1887   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1888   PetscCall(DMGetCoordinateDM(dm, &cdm));
1889   PetscCall(DMGetLocalSection(cdm, &coordSection));
1890   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1891   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1892   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1893 
1894   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1895   PetscCall(PetscDrawIsNull(draw, &isnull));
1896   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
1897   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1898 
1899   PetscCall(DMGetBoundingBox(dm, xyl, xyr));
1900   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1901   PetscCall(PetscDrawClear(draw));
1902 
1903   for (c = cStart; c < cEnd; ++c) {
1904     PetscScalar       *coords = NULL;
1905     const PetscScalar *coords_arr;
1906     PetscInt           numCoords;
1907     PetscBool          isDG;
1908 
1909     PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1910     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1911     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1912     PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1913   }
1914   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1915   PetscCall(PetscDrawFlush(draw));
1916   PetscCall(PetscDrawPause(draw));
1917   PetscCall(PetscDrawSave(draw));
1918   PetscFunctionReturn(PETSC_SUCCESS);
1919 }
1920 
1921 static PetscErrorCode DMPlexCreateHighOrderSurrogate_Internal(DM dm, DM *hdm)
1922 {
1923   DM           odm = dm, rdm = dm, cdm;
1924   PetscFE      fe;
1925   PetscSpace   sp;
1926   PetscClassId id;
1927   PetscInt     degree;
1928   PetscBool    hoView = PETSC_TRUE;
1929 
1930   PetscFunctionBegin;
1931   PetscObjectOptionsBegin((PetscObject)dm);
1932   PetscCall(PetscOptionsBool("-dm_plex_high_order_view", "Subsample to view meshes with high order coordinates", "DMPlexCreateHighOrderSurrogate_Internal", hoView, &hoView, NULL));
1933   PetscOptionsEnd();
1934   PetscCall(PetscObjectReference((PetscObject)dm));
1935   *hdm = dm;
1936   if (!hoView) PetscFunctionReturn(PETSC_SUCCESS);
1937   PetscCall(DMGetCoordinateDM(dm, &cdm));
1938   PetscCall(DMGetField(cdm, 0, NULL, (PetscObject *)&fe));
1939   PetscCall(PetscObjectGetClassId((PetscObject)fe, &id));
1940   if (id != PETSCFE_CLASSID) PetscFunctionReturn(PETSC_SUCCESS);
1941   PetscCall(PetscFEGetBasisSpace(fe, &sp));
1942   PetscCall(PetscSpaceGetDegree(sp, &degree, NULL));
1943   for (PetscInt r = 0, rd = PetscCeilReal(((PetscReal)degree) / 2.); r < (PetscInt)PetscCeilReal(PetscLog2Real(degree)); ++r, rd = PetscCeilReal(((PetscReal)rd) / 2.)) {
1944     DM  cdm, rcdm;
1945     Mat In;
1946     Vec cl, rcl;
1947 
1948     PetscCall(DMRefine(odm, PetscObjectComm((PetscObject)odm), &rdm));
1949     PetscCall(DMPlexCreateCoordinateSpace(rdm, rd, PETSC_FALSE, NULL));
1950     PetscCall(PetscObjectSetName((PetscObject)rdm, "Refined Mesh with Linear Coordinates"));
1951     PetscCall(DMGetCoordinateDM(odm, &cdm));
1952     PetscCall(DMGetCoordinateDM(rdm, &rcdm));
1953     PetscCall(DMGetCoordinatesLocal(odm, &cl));
1954     PetscCall(DMGetCoordinatesLocal(rdm, &rcl));
1955     PetscCall(DMSetCoarseDM(rcdm, cdm));
1956     PetscCall(DMCreateInterpolation(cdm, rcdm, &In, NULL));
1957     PetscCall(MatMult(In, cl, rcl));
1958     PetscCall(MatDestroy(&In));
1959     PetscCall(DMSetCoordinatesLocal(rdm, rcl));
1960     PetscCall(DMDestroy(&odm));
1961     odm = rdm;
1962   }
1963   *hdm = rdm;
1964   PetscFunctionReturn(PETSC_SUCCESS);
1965 }
1966 
1967 #if defined(PETSC_HAVE_EXODUSII)
1968   #include <exodusII.h>
1969   #include <petscviewerexodusii.h>
1970 #endif
1971 
1972 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1973 {
1974   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns;
1975   char      name[PETSC_MAX_PATH_LEN];
1976 
1977   PetscFunctionBegin;
1978   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1979   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1980   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
1981   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
1982   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1983   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
1984   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
1985   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
1986   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
1987   if (iascii) {
1988     PetscViewerFormat format;
1989     PetscCall(PetscViewerGetFormat(viewer, &format));
1990     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1991     else PetscCall(DMPlexView_Ascii(dm, viewer));
1992   } else if (ishdf5) {
1993 #if defined(PETSC_HAVE_HDF5)
1994     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1995 #else
1996     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1997 #endif
1998   } else if (isvtk) {
1999     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
2000   } else if (isdraw) {
2001     DM hdm;
2002 
2003     PetscCall(DMPlexCreateHighOrderSurrogate_Internal(dm, &hdm));
2004     PetscCall(DMPlexView_Draw(hdm, viewer));
2005     PetscCall(DMDestroy(&hdm));
2006   } else if (isglvis) {
2007     PetscCall(DMPlexView_GLVis(dm, viewer));
2008 #if defined(PETSC_HAVE_EXODUSII)
2009   } else if (isexodus) {
2010     /*
2011       exodusII requires that all sets be part of exactly one cell set.
2012       If the dm does not have a "Cell Sets" label defined, we create one
2013       with ID 1, containing all cells.
2014       Note that if the Cell Sets label is defined but does not cover all cells,
2015       we may still have a problem. This should probably be checked here or in the viewer;
2016     */
2017     PetscInt numCS;
2018     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
2019     if (!numCS) {
2020       PetscInt cStart, cEnd, c;
2021       PetscCall(DMCreateLabel(dm, "Cell Sets"));
2022       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
2023       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
2024     }
2025     PetscCall(DMView_PlexExodusII(dm, viewer));
2026 #endif
2027 #if defined(PETSC_HAVE_CGNS)
2028   } else if (iscgns) {
2029     PetscCall(DMView_PlexCGNS(dm, viewer));
2030 #endif
2031   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
2032   /* Optionally view the partition */
2033   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
2034   if (flg) {
2035     Vec ranks;
2036     PetscCall(DMPlexCreateRankField(dm, &ranks));
2037     PetscCall(VecView(ranks, viewer));
2038     PetscCall(VecDestroy(&ranks));
2039   }
2040   /* Optionally view a label */
2041   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
2042   if (flg) {
2043     DMLabel label;
2044     Vec     val;
2045 
2046     PetscCall(DMGetLabel(dm, name, &label));
2047     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
2048     PetscCall(DMPlexCreateLabelField(dm, label, &val));
2049     PetscCall(VecView(val, viewer));
2050     PetscCall(VecDestroy(&val));
2051   }
2052   PetscFunctionReturn(PETSC_SUCCESS);
2053 }
2054 
2055 /*@
2056   DMPlexTopologyView - Saves a `DMPLEX` topology into a file
2057 
2058   Collective
2059 
2060   Input Parameters:
2061 + dm     - The `DM` whose topology is to be saved
2062 - viewer - The `PetscViewer` to save it in
2063 
2064   Level: advanced
2065 
2066 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`, `PetscViewer`
2067 @*/
2068 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
2069 {
2070   PetscBool ishdf5;
2071 
2072   PetscFunctionBegin;
2073   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2074   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2075   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2076   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
2077   if (ishdf5) {
2078 #if defined(PETSC_HAVE_HDF5)
2079     PetscViewerFormat format;
2080     PetscCall(PetscViewerGetFormat(viewer, &format));
2081     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2082       IS globalPointNumbering;
2083 
2084       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2085       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
2086       PetscCall(ISDestroy(&globalPointNumbering));
2087     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2088 #else
2089     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2090 #endif
2091   }
2092   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
2093   PetscFunctionReturn(PETSC_SUCCESS);
2094 }
2095 
2096 /*@
2097   DMPlexCoordinatesView - Saves `DMPLEX` coordinates into a file
2098 
2099   Collective
2100 
2101   Input Parameters:
2102 + dm     - The `DM` whose coordinates are to be saved
2103 - viewer - The `PetscViewer` for saving
2104 
2105   Level: advanced
2106 
2107 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`, `PetscViewer`
2108 @*/
2109 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
2110 {
2111   PetscBool ishdf5;
2112 
2113   PetscFunctionBegin;
2114   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2115   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2116   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2117   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2118   if (ishdf5) {
2119 #if defined(PETSC_HAVE_HDF5)
2120     PetscViewerFormat format;
2121     PetscCall(PetscViewerGetFormat(viewer, &format));
2122     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2123       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
2124     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2125 #else
2126     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2127 #endif
2128   }
2129   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2130   PetscFunctionReturn(PETSC_SUCCESS);
2131 }
2132 
2133 /*@
2134   DMPlexLabelsView - Saves `DMPLEX` labels into a file
2135 
2136   Collective
2137 
2138   Input Parameters:
2139 + dm     - The `DM` whose labels are to be saved
2140 - viewer - The `PetscViewer` for saving
2141 
2142   Level: advanced
2143 
2144 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`, `PetscViewer`
2145 @*/
2146 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
2147 {
2148   PetscBool ishdf5;
2149 
2150   PetscFunctionBegin;
2151   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2152   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2153   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2154   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
2155   if (ishdf5) {
2156 #if defined(PETSC_HAVE_HDF5)
2157     IS                globalPointNumbering;
2158     PetscViewerFormat format;
2159 
2160     PetscCall(PetscViewerGetFormat(viewer, &format));
2161     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2162       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2163       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
2164       PetscCall(ISDestroy(&globalPointNumbering));
2165     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2166 #else
2167     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2168 #endif
2169   }
2170   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
2171   PetscFunctionReturn(PETSC_SUCCESS);
2172 }
2173 
2174 /*@
2175   DMPlexSectionView - Saves a section associated with a `DMPLEX`
2176 
2177   Collective
2178 
2179   Input Parameters:
2180 + dm        - The `DM` that contains the topology on which the section to be saved is defined
2181 . viewer    - The `PetscViewer` for saving
2182 - sectiondm - The `DM` that contains the section to be saved, can be `NULL`
2183 
2184   Level: advanced
2185 
2186   Notes:
2187   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.
2188 
2189   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.
2190 
2191 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`, `PetscViewer`
2192 @*/
2193 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
2194 {
2195   PetscBool ishdf5;
2196 
2197   PetscFunctionBegin;
2198   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2199   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2200   if (!sectiondm) sectiondm = dm;
2201   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2202   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2203   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
2204   if (ishdf5) {
2205 #if defined(PETSC_HAVE_HDF5)
2206     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
2207 #else
2208     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2209 #endif
2210   }
2211   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
2212   PetscFunctionReturn(PETSC_SUCCESS);
2213 }
2214 
2215 /*@
2216   DMPlexGlobalVectorView - Saves a global vector
2217 
2218   Collective
2219 
2220   Input Parameters:
2221 + dm        - The `DM` that represents the topology
2222 . viewer    - The `PetscViewer` to save data with
2223 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2224 - vec       - The global vector to be saved
2225 
2226   Level: advanced
2227 
2228   Notes:
2229   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.
2230 
2231   Calling sequence:
2232 .vb
2233        DMCreate(PETSC_COMM_WORLD, &dm);
2234        DMSetType(dm, DMPLEX);
2235        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2236        DMClone(dm, &sectiondm);
2237        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2238        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2239        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2240        PetscSectionSetChart(section, pStart, pEnd);
2241        PetscSectionSetUp(section);
2242        DMSetLocalSection(sectiondm, section);
2243        PetscSectionDestroy(&section);
2244        DMGetGlobalVector(sectiondm, &vec);
2245        PetscObjectSetName((PetscObject)vec, "vec_name");
2246        DMPlexTopologyView(dm, viewer);
2247        DMPlexSectionView(dm, viewer, sectiondm);
2248        DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
2249        DMRestoreGlobalVector(sectiondm, &vec);
2250        DMDestroy(&sectiondm);
2251        DMDestroy(&dm);
2252 .ve
2253 
2254 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2255 @*/
2256 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2257 {
2258   PetscBool ishdf5;
2259 
2260   PetscFunctionBegin;
2261   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2262   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2263   if (!sectiondm) sectiondm = dm;
2264   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2265   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2266   /* Check consistency */
2267   {
2268     PetscSection section;
2269     PetscBool    includesConstraints;
2270     PetscInt     m, m1;
2271 
2272     PetscCall(VecGetLocalSize(vec, &m1));
2273     PetscCall(DMGetGlobalSection(sectiondm, &section));
2274     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2275     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2276     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2277     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2278   }
2279   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2280   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2281   if (ishdf5) {
2282 #if defined(PETSC_HAVE_HDF5)
2283     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2284 #else
2285     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2286 #endif
2287   }
2288   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2289   PetscFunctionReturn(PETSC_SUCCESS);
2290 }
2291 
2292 /*@
2293   DMPlexLocalVectorView - Saves a local vector
2294 
2295   Collective
2296 
2297   Input Parameters:
2298 + dm        - The `DM` that represents the topology
2299 . viewer    - The `PetscViewer` to save data with
2300 . sectiondm - The `DM` that contains the local section on which `vec` is defined, can be `NULL`
2301 - vec       - The local vector to be saved
2302 
2303   Level: advanced
2304 
2305   Note:
2306   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.
2307 
2308   Calling sequence:
2309 .vb
2310        DMCreate(PETSC_COMM_WORLD, &dm);
2311        DMSetType(dm, DMPLEX);
2312        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2313        DMClone(dm, &sectiondm);
2314        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2315        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2316        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2317        PetscSectionSetChart(section, pStart, pEnd);
2318        PetscSectionSetUp(section);
2319        DMSetLocalSection(sectiondm, section);
2320        DMGetLocalVector(sectiondm, &vec);
2321        PetscObjectSetName((PetscObject)vec, "vec_name");
2322        DMPlexTopologyView(dm, viewer);
2323        DMPlexSectionView(dm, viewer, sectiondm);
2324        DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2325        DMRestoreLocalVector(sectiondm, &vec);
2326        DMDestroy(&sectiondm);
2327        DMDestroy(&dm);
2328 .ve
2329 
2330 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2331 @*/
2332 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2333 {
2334   PetscBool ishdf5;
2335 
2336   PetscFunctionBegin;
2337   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2338   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2339   if (!sectiondm) sectiondm = dm;
2340   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2341   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2342   /* Check consistency */
2343   {
2344     PetscSection section;
2345     PetscBool    includesConstraints;
2346     PetscInt     m, m1;
2347 
2348     PetscCall(VecGetLocalSize(vec, &m1));
2349     PetscCall(DMGetLocalSection(sectiondm, &section));
2350     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2351     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2352     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2353     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2354   }
2355   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2356   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2357   if (ishdf5) {
2358 #if defined(PETSC_HAVE_HDF5)
2359     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2360 #else
2361     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2362 #endif
2363   }
2364   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2365   PetscFunctionReturn(PETSC_SUCCESS);
2366 }
2367 
2368 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2369 {
2370   PetscBool ishdf5;
2371 
2372   PetscFunctionBegin;
2373   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2374   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2375   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2376   if (ishdf5) {
2377 #if defined(PETSC_HAVE_HDF5)
2378     PetscViewerFormat format;
2379     PetscCall(PetscViewerGetFormat(viewer, &format));
2380     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2381       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2382     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2383       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2384     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2385     PetscFunctionReturn(PETSC_SUCCESS);
2386 #else
2387     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2388 #endif
2389   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2390 }
2391 
2392 /*@
2393   DMPlexTopologyLoad - Loads a topology into a `DMPLEX`
2394 
2395   Collective
2396 
2397   Input Parameters:
2398 + dm     - The `DM` into which the topology is loaded
2399 - viewer - The `PetscViewer` for the saved topology
2400 
2401   Output Parameter:
2402 . 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;
2403   `NULL` if unneeded
2404 
2405   Level: advanced
2406 
2407 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2408           `PetscViewer`, `PetscSF`
2409 @*/
2410 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2411 {
2412   PetscBool ishdf5;
2413 
2414   PetscFunctionBegin;
2415   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2416   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2417   if (globalToLocalPointSF) PetscAssertPointer(globalToLocalPointSF, 3);
2418   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2419   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2420   if (ishdf5) {
2421 #if defined(PETSC_HAVE_HDF5)
2422     PetscViewerFormat format;
2423     PetscCall(PetscViewerGetFormat(viewer, &format));
2424     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2425       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2426     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2427 #else
2428     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2429 #endif
2430   }
2431   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2432   PetscFunctionReturn(PETSC_SUCCESS);
2433 }
2434 
2435 /*@
2436   DMPlexCoordinatesLoad - Loads coordinates into a `DMPLEX`
2437 
2438   Collective
2439 
2440   Input Parameters:
2441 + dm                   - The `DM` into which the coordinates are loaded
2442 . viewer               - The `PetscViewer` for the saved coordinates
2443 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2444 
2445   Level: advanced
2446 
2447 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2448           `PetscSF`, `PetscViewer`
2449 @*/
2450 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2451 {
2452   PetscBool ishdf5;
2453 
2454   PetscFunctionBegin;
2455   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2456   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2457   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2458   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2459   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2460   if (ishdf5) {
2461 #if defined(PETSC_HAVE_HDF5)
2462     PetscViewerFormat format;
2463     PetscCall(PetscViewerGetFormat(viewer, &format));
2464     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2465       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2466     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2467 #else
2468     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2469 #endif
2470   }
2471   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2472   PetscFunctionReturn(PETSC_SUCCESS);
2473 }
2474 
2475 /*@
2476   DMPlexLabelsLoad - Loads labels into a `DMPLEX`
2477 
2478   Collective
2479 
2480   Input Parameters:
2481 + dm                   - The `DM` into which the labels are loaded
2482 . viewer               - The `PetscViewer` for the saved labels
2483 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading `dm` from viewer
2484 
2485   Level: advanced
2486 
2487   Note:
2488   The `PetscSF` argument must not be `NULL` if the `DM` is distributed, otherwise an error occurs.
2489 
2490 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2491           `PetscSF`, `PetscViewer`
2492 @*/
2493 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2494 {
2495   PetscBool ishdf5;
2496 
2497   PetscFunctionBegin;
2498   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2499   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2500   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2501   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2502   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2503   if (ishdf5) {
2504 #if defined(PETSC_HAVE_HDF5)
2505     PetscViewerFormat format;
2506 
2507     PetscCall(PetscViewerGetFormat(viewer, &format));
2508     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2509       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2510     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2511 #else
2512     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2513 #endif
2514   }
2515   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2516   PetscFunctionReturn(PETSC_SUCCESS);
2517 }
2518 
2519 /*@
2520   DMPlexSectionLoad - Loads section into a `DMPLEX`
2521 
2522   Collective
2523 
2524   Input Parameters:
2525 + dm                   - The `DM` that represents the topology
2526 . viewer               - The `PetscViewer` that represents the on-disk section (sectionA)
2527 . sectiondm            - The `DM` into which the on-disk section (sectionA) is migrated, can be `NULL`
2528 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad(`) when loading dm from viewer
2529 
2530   Output Parameters:
2531 + 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)
2532 - 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)
2533 
2534   Level: advanced
2535 
2536   Notes:
2537   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.
2538 
2539   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.
2540 
2541   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.
2542 
2543   Example using 2 processes:
2544 .vb
2545   NX (number of points on dm): 4
2546   sectionA                   : the on-disk section
2547   vecA                       : a vector associated with sectionA
2548   sectionB                   : sectiondm's local section constructed in this function
2549   vecB (local)               : a vector associated with sectiondm's local section
2550   vecB (global)              : a vector associated with sectiondm's global section
2551 
2552                                      rank 0    rank 1
2553   vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2554   sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2555   sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2556   sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2557   [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2558   sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2559   sectionB->atlasDof             :     1 0 1 | 1 3
2560   sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2561   vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2562   vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2563 .ve
2564   where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2565 
2566 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`, `PetscSF`, `PetscViewer`
2567 @*/
2568 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
2569 {
2570   PetscBool ishdf5;
2571 
2572   PetscFunctionBegin;
2573   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2574   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2575   if (!sectiondm) sectiondm = dm;
2576   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2577   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2578   if (globalDofSF) PetscAssertPointer(globalDofSF, 5);
2579   if (localDofSF) PetscAssertPointer(localDofSF, 6);
2580   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2581   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2582   if (ishdf5) {
2583 #if defined(PETSC_HAVE_HDF5)
2584     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2585 #else
2586     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2587 #endif
2588   }
2589   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2590   PetscFunctionReturn(PETSC_SUCCESS);
2591 }
2592 
2593 /*@
2594   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2595 
2596   Collective
2597 
2598   Input Parameters:
2599 + dm        - The `DM` that represents the topology
2600 . viewer    - The `PetscViewer` that represents the on-disk vector data
2601 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2602 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2603 - vec       - The global vector to set values of
2604 
2605   Level: advanced
2606 
2607   Notes:
2608   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object (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.
2609 
2610   Calling sequence:
2611 .vb
2612        DMCreate(PETSC_COMM_WORLD, &dm);
2613        DMSetType(dm, DMPLEX);
2614        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2615        DMPlexTopologyLoad(dm, viewer, &sfX);
2616        DMClone(dm, &sectiondm);
2617        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2618        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2619        DMGetGlobalVector(sectiondm, &vec);
2620        PetscObjectSetName((PetscObject)vec, "vec_name");
2621        DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2622        DMRestoreGlobalVector(sectiondm, &vec);
2623        PetscSFDestroy(&gsf);
2624        PetscSFDestroy(&sfX);
2625        DMDestroy(&sectiondm);
2626        DMDestroy(&dm);
2627 .ve
2628 
2629 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2630           `PetscSF`, `PetscViewer`
2631 @*/
2632 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2633 {
2634   PetscBool ishdf5;
2635 
2636   PetscFunctionBegin;
2637   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2638   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2639   if (!sectiondm) sectiondm = dm;
2640   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2641   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2642   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2643   /* Check consistency */
2644   {
2645     PetscSection section;
2646     PetscBool    includesConstraints;
2647     PetscInt     m, m1;
2648 
2649     PetscCall(VecGetLocalSize(vec, &m1));
2650     PetscCall(DMGetGlobalSection(sectiondm, &section));
2651     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2652     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2653     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2654     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2655   }
2656   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2657   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2658   if (ishdf5) {
2659 #if defined(PETSC_HAVE_HDF5)
2660     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2661 #else
2662     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2663 #endif
2664   }
2665   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2666   PetscFunctionReturn(PETSC_SUCCESS);
2667 }
2668 
2669 /*@
2670   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2671 
2672   Collective
2673 
2674   Input Parameters:
2675 + dm        - The `DM` that represents the topology
2676 . viewer    - The `PetscViewer` that represents the on-disk vector data
2677 . sectiondm - The `DM` that contains the local section on which vec is defined, can be `NULL`
2678 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2679 - vec       - The local vector to set values of
2680 
2681   Level: advanced
2682 
2683   Notes:
2684   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.
2685 
2686   Calling sequence:
2687 .vb
2688        DMCreate(PETSC_COMM_WORLD, &dm);
2689        DMSetType(dm, DMPLEX);
2690        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2691        DMPlexTopologyLoad(dm, viewer, &sfX);
2692        DMClone(dm, &sectiondm);
2693        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2694        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2695        DMGetLocalVector(sectiondm, &vec);
2696        PetscObjectSetName((PetscObject)vec, "vec_name");
2697        DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2698        DMRestoreLocalVector(sectiondm, &vec);
2699        PetscSFDestroy(&lsf);
2700        PetscSFDestroy(&sfX);
2701        DMDestroy(&sectiondm);
2702        DMDestroy(&dm);
2703 .ve
2704 
2705 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2706           `PetscSF`, `PetscViewer`
2707 @*/
2708 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2709 {
2710   PetscBool ishdf5;
2711 
2712   PetscFunctionBegin;
2713   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2714   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2715   if (!sectiondm) sectiondm = dm;
2716   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2717   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2718   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2719   /* Check consistency */
2720   {
2721     PetscSection section;
2722     PetscBool    includesConstraints;
2723     PetscInt     m, m1;
2724 
2725     PetscCall(VecGetLocalSize(vec, &m1));
2726     PetscCall(DMGetLocalSection(sectiondm, &section));
2727     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2728     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2729     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2730     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2731   }
2732   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2733   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2734   if (ishdf5) {
2735 #if defined(PETSC_HAVE_HDF5)
2736     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2737 #else
2738     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2739 #endif
2740   }
2741   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2742   PetscFunctionReturn(PETSC_SUCCESS);
2743 }
2744 
2745 PetscErrorCode DMDestroy_Plex(DM dm)
2746 {
2747   DM_Plex *mesh = (DM_Plex *)dm->data;
2748 
2749   PetscFunctionBegin;
2750   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2751   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2752   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2753   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2754   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerivativeBoundaryValues_C", NULL));
2755   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2756   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2757   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2758   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2759   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2760   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2761   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetDefault_C", NULL));
2762   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetDefault_C", NULL));
2763   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetType_C", NULL));
2764   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetType_C", NULL));
2765   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2766   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2767   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetUseCeed_C", NULL));
2768   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetUseCeed_C", NULL));
2769   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", NULL));
2770   if (--mesh->refct > 0) PetscFunctionReturn(PETSC_SUCCESS);
2771   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2772   PetscCall(PetscFree(mesh->cones));
2773   PetscCall(PetscFree(mesh->coneOrientations));
2774   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2775   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2776   PetscCall(PetscFree(mesh->supports));
2777   PetscCall(PetscFree(mesh->cellTypes));
2778   PetscCall(DMPlexTransformDestroy(&mesh->tr));
2779   PetscCall(PetscFree(mesh->tetgenOpts));
2780   PetscCall(PetscFree(mesh->triangleOpts));
2781   PetscCall(PetscFree(mesh->transformType));
2782   PetscCall(PetscFree(mesh->distributionName));
2783   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2784   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2785   PetscCall(ISDestroy(&mesh->subpointIS));
2786   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2787   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2788   if (mesh->periodic.face_sfs) {
2789     for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(PetscSFDestroy(&mesh->periodic.face_sfs[i]));
2790     PetscCall(PetscFree(mesh->periodic.face_sfs));
2791   }
2792   PetscCall(PetscSFDestroy(&mesh->periodic.composed_sf));
2793   if (mesh->periodic.periodic_points) {
2794     for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(ISDestroy(&mesh->periodic.periodic_points[i]));
2795     PetscCall(PetscFree(mesh->periodic.periodic_points));
2796   }
2797   if (mesh->periodic.transform) PetscCall(PetscFree(mesh->periodic.transform));
2798   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2799   PetscCall(ISDestroy(&mesh->anchorIS));
2800   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2801   PetscCall(PetscFree(mesh->parents));
2802   PetscCall(PetscFree(mesh->childIDs));
2803   PetscCall(PetscSectionDestroy(&mesh->childSection));
2804   PetscCall(PetscFree(mesh->children));
2805   PetscCall(DMDestroy(&mesh->referenceTree));
2806   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2807   PetscCall(PetscFree(mesh->neighbors));
2808   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2809   if (mesh->nonempty_comm != MPI_COMM_NULL && mesh->nonempty_comm != MPI_COMM_SELF) PetscCallMPI(MPI_Comm_free(&mesh->nonempty_comm));
2810   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2811   PetscCall(PetscFree(mesh));
2812   PetscFunctionReturn(PETSC_SUCCESS);
2813 }
2814 
2815 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2816 {
2817   PetscSection           sectionGlobal, sectionLocal;
2818   PetscInt               bs = -1, mbs;
2819   PetscInt               localSize, localStart = 0;
2820   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2821   MatType                mtype;
2822   ISLocalToGlobalMapping ltog;
2823 
2824   PetscFunctionBegin;
2825   PetscCall(MatInitializePackage());
2826   mtype = dm->mattype;
2827   PetscCall(DMGetLocalSection(dm, &sectionLocal));
2828   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2829   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2830   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2831   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2832   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2833   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2834   PetscCall(MatSetType(*J, mtype));
2835   PetscCall(MatSetFromOptions(*J));
2836   PetscCall(MatGetBlockSize(*J, &mbs));
2837   if (mbs > 1) bs = mbs;
2838   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2839   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2840   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2841   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2842   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2843   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2844   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2845   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2846   if (!isShell) {
2847     // There are three states with pblocks, since block starts can have no dofs:
2848     // UNKNOWN) New Block:   An open block has been signalled by pblocks[p] == 1
2849     // TRUE)    Block Start: The first entry in a block has been added
2850     // FALSE)   Block Add:   An additional block entry has been added, since pblocks[p] == 0
2851     PetscBT         blst;
2852     PetscBool3      bstate     = PETSC_BOOL3_UNKNOWN;
2853     PetscBool       fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2854     const PetscInt *perm       = NULL;
2855     PetscInt       *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2856     PetscInt        pStart, pEnd, dof, cdof, num_fields;
2857 
2858     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2859     PetscCall(PetscSectionGetBlockStarts(sectionLocal, &blst));
2860     if (sectionLocal->perm) PetscCall(ISGetIndices(sectionLocal->perm, &perm));
2861 
2862     PetscCall(PetscCalloc1(localSize, &pblocks));
2863     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2864     PetscCall(PetscSectionGetNumFields(sectionGlobal, &num_fields));
2865     // We need to process in the permuted order to get block sizes right
2866     for (PetscInt point = pStart; point < pEnd; ++point) {
2867       const PetscInt p = perm ? perm[point] : point;
2868 
2869       switch (dm->blocking_type) {
2870       case DM_BLOCKING_TOPOLOGICAL_POINT: { // One block per topological point
2871         PetscInt bdof, offset;
2872 
2873         PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2874         PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2875         PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2876         if (blst && PetscBTLookup(blst, p)) bstate = PETSC_BOOL3_UNKNOWN;
2877         if (dof > 0) {
2878           // State change
2879           if (bstate == PETSC_BOOL3_UNKNOWN) bstate = PETSC_BOOL3_TRUE;
2880           else if (bstate == PETSC_BOOL3_TRUE && blst && !PetscBTLookup(blst, p)) bstate = PETSC_BOOL3_FALSE;
2881 
2882           for (PetscInt i = 0; i < dof - cdof; ++i) pblocks[offset - localStart + i] = dof - cdof;
2883           // Signal block concatenation
2884           if (bstate == PETSC_BOOL3_FALSE && dof - cdof) pblocks[offset - localStart] = -(dof - cdof);
2885         }
2886         dof  = dof < 0 ? -(dof + 1) : dof;
2887         bdof = cdof && (dof - cdof) ? 1 : dof;
2888         if (dof) {
2889           if (bs < 0) {
2890             bs = bdof;
2891           } else if (bs != bdof) {
2892             bs = 1;
2893           }
2894         }
2895       } break;
2896       case DM_BLOCKING_FIELD_NODE: {
2897         for (PetscInt field = 0; field < num_fields; field++) {
2898           PetscInt num_comp, bdof, offset;
2899           PetscCall(PetscSectionGetFieldComponents(sectionGlobal, field, &num_comp));
2900           PetscCall(PetscSectionGetFieldDof(sectionGlobal, p, field, &dof));
2901           if (dof < 0) continue;
2902           PetscCall(PetscSectionGetFieldOffset(sectionGlobal, p, field, &offset));
2903           PetscCall(PetscSectionGetFieldConstraintDof(sectionGlobal, p, field, &cdof));
2904           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);
2905           PetscInt num_nodes = dof / num_comp;
2906           for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = (dof - cdof) / num_nodes;
2907           // Handle possibly constant block size (unlikely)
2908           bdof = cdof && (dof - cdof) ? 1 : dof;
2909           if (dof) {
2910             if (bs < 0) {
2911               bs = bdof;
2912             } else if (bs != bdof) {
2913               bs = 1;
2914             }
2915           }
2916         }
2917       } break;
2918       }
2919     }
2920     if (sectionLocal->perm) PetscCall(ISRestoreIndices(sectionLocal->perm, &perm));
2921     /* Must have same blocksize on all procs (some might have no points) */
2922     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2923     bsLocal[1] = bs;
2924     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
2925     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2926     else bs = bsMinMax[0];
2927     bs = PetscMax(1, bs);
2928     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
2929     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2930       PetscCall(MatSetBlockSize(*J, bs));
2931       PetscCall(MatSetUp(*J));
2932     } else {
2933       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
2934       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2935       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2936     }
2937     if (pblocks) { // Consolidate blocks
2938       PetscInt nblocks = 0;
2939       pblocks[0]       = PetscAbs(pblocks[0]);
2940       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
2941         if (pblocks[i] == 0) continue;
2942         // Negative block size indicates the blocks should be concatenated
2943         if (pblocks[i] < 0) {
2944           pblocks[i] = -pblocks[i];
2945           pblocks[nblocks - 1] += pblocks[i];
2946         } else {
2947           pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2948         }
2949         for (PetscInt j = 1; j < pblocks[i]; j++)
2950           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);
2951       }
2952       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2953     }
2954     PetscCall(PetscFree(pblocks));
2955   }
2956   PetscCall(MatSetDM(*J, dm));
2957   PetscFunctionReturn(PETSC_SUCCESS);
2958 }
2959 
2960 /*@
2961   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2962 
2963   Not Collective
2964 
2965   Input Parameter:
2966 . dm - The `DMPLEX`
2967 
2968   Output Parameter:
2969 . subsection - The subdomain section
2970 
2971   Level: developer
2972 
2973 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscSection`
2974 @*/
2975 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2976 {
2977   DM_Plex *mesh = (DM_Plex *)dm->data;
2978 
2979   PetscFunctionBegin;
2980   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2981   if (!mesh->subdomainSection) {
2982     PetscSection section;
2983     PetscSF      sf;
2984 
2985     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
2986     PetscCall(DMGetLocalSection(dm, &section));
2987     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
2988     PetscCall(PetscSFDestroy(&sf));
2989   }
2990   *subsection = mesh->subdomainSection;
2991   PetscFunctionReturn(PETSC_SUCCESS);
2992 }
2993 
2994 /*@
2995   DMPlexGetChart - Return the interval for all mesh points [`pStart`, `pEnd`)
2996 
2997   Not Collective
2998 
2999   Input Parameter:
3000 . dm - The `DMPLEX`
3001 
3002   Output Parameters:
3003 + pStart - The first mesh point
3004 - pEnd   - The upper bound for mesh points
3005 
3006   Level: beginner
3007 
3008 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`
3009 @*/
3010 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
3011 {
3012   DM_Plex *mesh = (DM_Plex *)dm->data;
3013 
3014   PetscFunctionBegin;
3015   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3016   if (mesh->tr) PetscCall(DMPlexTransformGetChart(mesh->tr, pStart, pEnd));
3017   else PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
3018   PetscFunctionReturn(PETSC_SUCCESS);
3019 }
3020 
3021 /*@
3022   DMPlexSetChart - Set the interval for all mesh points [`pStart`, `pEnd`)
3023 
3024   Not Collective
3025 
3026   Input Parameters:
3027 + dm     - The `DMPLEX`
3028 . pStart - The first mesh point
3029 - pEnd   - The upper bound for mesh points
3030 
3031   Level: beginner
3032 
3033 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetChart()`
3034 @*/
3035 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
3036 {
3037   DM_Plex *mesh = (DM_Plex *)dm->data;
3038 
3039   PetscFunctionBegin;
3040   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3041   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
3042   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
3043   PetscCall(PetscFree(mesh->cellTypes));
3044   PetscFunctionReturn(PETSC_SUCCESS);
3045 }
3046 
3047 /*@
3048   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
3049 
3050   Not Collective
3051 
3052   Input Parameters:
3053 + dm - The `DMPLEX`
3054 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3055 
3056   Output Parameter:
3057 . size - The cone size for point `p`
3058 
3059   Level: beginner
3060 
3061 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3062 @*/
3063 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
3064 {
3065   DM_Plex *mesh = (DM_Plex *)dm->data;
3066 
3067   PetscFunctionBegin;
3068   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3069   PetscAssertPointer(size, 3);
3070   if (mesh->tr) PetscCall(DMPlexTransformGetConeSize(mesh->tr, p, size));
3071   else PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
3072   PetscFunctionReturn(PETSC_SUCCESS);
3073 }
3074 
3075 /*@
3076   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
3077 
3078   Not Collective
3079 
3080   Input Parameters:
3081 + dm   - The `DMPLEX`
3082 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3083 - size - The cone size for point `p`
3084 
3085   Level: beginner
3086 
3087   Note:
3088   This should be called after `DMPlexSetChart()`.
3089 
3090 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
3091 @*/
3092 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
3093 {
3094   DM_Plex *mesh = (DM_Plex *)dm->data;
3095 
3096   PetscFunctionBegin;
3097   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3098   PetscCheck(!mesh->tr, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Cannot call DMPlexSetConeSize() on a mesh with a transform defined.");
3099   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
3100   PetscFunctionReturn(PETSC_SUCCESS);
3101 }
3102 
3103 /*@C
3104   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
3105 
3106   Not Collective
3107 
3108   Input Parameters:
3109 + dm - The `DMPLEX`
3110 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3111 
3112   Output Parameter:
3113 . cone - An array of points which are on the in-edges for point `p`, the length of `cone` is the result of `DMPlexGetConeSize()`
3114 
3115   Level: beginner
3116 
3117   Fortran Notes:
3118   `cone` must be declared with
3119 .vb
3120   PetscInt, pointer :: cone(:)
3121 .ve
3122 
3123   You must also call `DMPlexRestoreCone()` after you finish using the array.
3124   `DMPlexRestoreCone()` is not needed/available in C.
3125 
3126 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`, `DMPlexRestoreCone()`
3127 @*/
3128 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
3129 {
3130   DM_Plex *mesh = (DM_Plex *)dm->data;
3131   PetscInt off;
3132 
3133   PetscFunctionBegin;
3134   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3135   PetscAssertPointer(cone, 3);
3136   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3137   *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3138   PetscFunctionReturn(PETSC_SUCCESS);
3139 }
3140 
3141 /*@
3142   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
3143 
3144   Not Collective
3145 
3146   Input Parameters:
3147 + dm - The `DMPLEX`
3148 - p  - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3149 
3150   Output Parameters:
3151 + pConesSection - `PetscSection` describing the layout of `pCones`
3152 - pCones        - An `IS` containing the points which are on the in-edges for the point set `p`
3153 
3154   Level: intermediate
3155 
3156 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`, `PetscSection`, `IS`
3157 @*/
3158 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
3159 {
3160   PetscSection cs, newcs;
3161   PetscInt    *cones;
3162   PetscInt    *newarr = NULL;
3163   PetscInt     n;
3164 
3165   PetscFunctionBegin;
3166   PetscCall(DMPlexGetCones(dm, &cones));
3167   PetscCall(DMPlexGetConeSection(dm, &cs));
3168   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
3169   if (pConesSection) *pConesSection = newcs;
3170   if (pCones) {
3171     PetscCall(PetscSectionGetStorageSize(newcs, &n));
3172     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
3173   }
3174   PetscFunctionReturn(PETSC_SUCCESS);
3175 }
3176 
3177 /*@
3178   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
3179 
3180   Not Collective
3181 
3182   Input Parameters:
3183 + dm     - The `DMPLEX`
3184 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3185 
3186   Output Parameter:
3187 . expandedPoints - An `IS` containing the of vertices recursively expanded from input points
3188 
3189   Level: advanced
3190 
3191   Notes:
3192   Like `DMPlexGetConeRecursive()` but returns only the 0-depth `IS` (i.e. vertices only) and no sections.
3193 
3194   There is no corresponding Restore function, just call `ISDestroy()` on the returned `IS` to deallocate.
3195 
3196 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`,
3197           `DMPlexGetDepth()`, `IS`
3198 @*/
3199 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
3200 {
3201   IS      *expandedPointsAll;
3202   PetscInt depth;
3203 
3204   PetscFunctionBegin;
3205   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3206   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3207   PetscAssertPointer(expandedPoints, 3);
3208   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3209   *expandedPoints = expandedPointsAll[0];
3210   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
3211   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3212   PetscFunctionReturn(PETSC_SUCCESS);
3213 }
3214 
3215 /*@
3216   DMPlexGetConeRecursive - Expand each given point into its cone points and do that recursively until we end up just with vertices
3217   (DAG points of depth 0, i.e., without cones).
3218 
3219   Not Collective
3220 
3221   Input Parameters:
3222 + dm     - The `DMPLEX`
3223 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3224 
3225   Output Parameters:
3226 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3227 . expandedPoints - (optional) An array of index sets with recursively expanded cones
3228 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3229 
3230   Level: advanced
3231 
3232   Notes:
3233   Like `DMPlexGetConeTuple()` but recursive.
3234 
3235   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.
3236   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
3237 
3238   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\:
3239   (1) DAG points in `expandedPoints`[d+1] with `depth` d+1 to their cone points in `expandedPoints`[d];
3240   (2) DAG points in `expandedPoints`[d+1] with `depth` in [0,d] to the same points in `expandedPoints`[d].
3241 
3242 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3243           `DMPlexGetDepth()`, `PetscSection`, `IS`
3244 @*/
3245 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3246 {
3247   const PetscInt *arr0 = NULL, *cone = NULL;
3248   PetscInt       *arr = NULL, *newarr = NULL;
3249   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
3250   IS             *expandedPoints_;
3251   PetscSection   *sections_;
3252 
3253   PetscFunctionBegin;
3254   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3255   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3256   if (depth) PetscAssertPointer(depth, 3);
3257   if (expandedPoints) PetscAssertPointer(expandedPoints, 4);
3258   if (sections) PetscAssertPointer(sections, 5);
3259   PetscCall(ISGetLocalSize(points, &n));
3260   PetscCall(ISGetIndices(points, &arr0));
3261   PetscCall(DMPlexGetDepth(dm, &depth_));
3262   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
3263   PetscCall(PetscCalloc1(depth_, &sections_));
3264   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
3265   for (d = depth_ - 1; d >= 0; d--) {
3266     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
3267     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
3268     for (i = 0; i < n; i++) {
3269       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
3270       if (arr[i] >= start && arr[i] < end) {
3271         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
3272         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
3273       } else {
3274         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
3275       }
3276     }
3277     PetscCall(PetscSectionSetUp(sections_[d]));
3278     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
3279     PetscCall(PetscMalloc1(newn, &newarr));
3280     for (i = 0; i < n; i++) {
3281       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
3282       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
3283       if (cn > 1) {
3284         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
3285         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
3286       } else {
3287         newarr[co] = arr[i];
3288       }
3289     }
3290     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
3291     arr = newarr;
3292     n   = newn;
3293   }
3294   PetscCall(ISRestoreIndices(points, &arr0));
3295   *depth = depth_;
3296   if (expandedPoints) *expandedPoints = expandedPoints_;
3297   else {
3298     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
3299     PetscCall(PetscFree(expandedPoints_));
3300   }
3301   if (sections) *sections = sections_;
3302   else {
3303     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
3304     PetscCall(PetscFree(sections_));
3305   }
3306   PetscFunctionReturn(PETSC_SUCCESS);
3307 }
3308 
3309 /*@
3310   DMPlexRestoreConeRecursive - Deallocates arrays created by `DMPlexGetConeRecursive()`
3311 
3312   Not Collective
3313 
3314   Input Parameters:
3315 + dm     - The `DMPLEX`
3316 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3317 
3318   Output Parameters:
3319 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3320 . expandedPoints - (optional) An array of recursively expanded cones
3321 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3322 
3323   Level: advanced
3324 
3325   Note:
3326   See `DMPlexGetConeRecursive()`
3327 
3328 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3329           `DMPlexGetDepth()`, `IS`, `PetscSection`
3330 @*/
3331 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3332 {
3333   PetscInt d, depth_;
3334 
3335   PetscFunctionBegin;
3336   PetscCall(DMPlexGetDepth(dm, &depth_));
3337   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
3338   if (depth) *depth = 0;
3339   if (expandedPoints) {
3340     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
3341     PetscCall(PetscFree(*expandedPoints));
3342   }
3343   if (sections) {
3344     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
3345     PetscCall(PetscFree(*sections));
3346   }
3347   PetscFunctionReturn(PETSC_SUCCESS);
3348 }
3349 
3350 /*@
3351   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
3352 
3353   Not Collective
3354 
3355   Input Parameters:
3356 + dm   - The `DMPLEX`
3357 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3358 - cone - An array of points which are on the in-edges for point `p`, its length must have been previously provided with `DMPlexSetConeSize()`
3359 
3360   Level: beginner
3361 
3362   Note:
3363   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3364 
3365 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3366 @*/
3367 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3368 {
3369   DM_Plex *mesh = (DM_Plex *)dm->data;
3370   PetscInt dof, off, c;
3371 
3372   PetscFunctionBegin;
3373   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3374   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3375   if (dof) PetscAssertPointer(cone, 3);
3376   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3377   if (PetscDefined(USE_DEBUG)) {
3378     PetscInt pStart, pEnd;
3379     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3380     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);
3381     for (c = 0; c < dof; ++c) {
3382       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);
3383       mesh->cones[off + c] = cone[c];
3384     }
3385   } else {
3386     for (c = 0; c < dof; ++c) mesh->cones[off + c] = cone[c];
3387   }
3388   PetscFunctionReturn(PETSC_SUCCESS);
3389 }
3390 
3391 /*@C
3392   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3393 
3394   Not Collective
3395 
3396   Input Parameters:
3397 + dm - The `DMPLEX`
3398 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3399 
3400   Output Parameter:
3401 . coneOrientation - An array of orientations which are on the in-edges for point `p`. An orientation is an
3402                     integer giving the prescription for cone traversal. Its length is given by the result of `DMPlexSetConeSize()`
3403 
3404   Level: beginner
3405 
3406   Note:
3407   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3408   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3409   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3410   with the identity.
3411 
3412   Fortran Notes:
3413   You must call `DMPlexRestoreConeOrientation()` after you finish using the returned array.
3414   `DMPlexRestoreConeOrientation()` is not needed/available in C.
3415 
3416 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetConeSize()`, `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`,
3417           `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3418 @*/
3419 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3420 {
3421   DM_Plex *mesh = (DM_Plex *)dm->data;
3422   PetscInt off;
3423 
3424   PetscFunctionBegin;
3425   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3426   if (PetscDefined(USE_DEBUG)) {
3427     PetscInt dof;
3428     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3429     if (dof) PetscAssertPointer(coneOrientation, 3);
3430   }
3431   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3432 
3433   *coneOrientation = &mesh->coneOrientations[off];
3434   PetscFunctionReturn(PETSC_SUCCESS);
3435 }
3436 
3437 /*@
3438   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3439 
3440   Not Collective
3441 
3442   Input Parameters:
3443 + dm              - The `DMPLEX`
3444 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3445 - coneOrientation - An array of orientations. Its length is given by the result of `DMPlexSetConeSize()`
3446 
3447   Level: beginner
3448 
3449   Notes:
3450   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3451 
3452   The meaning of coneOrientation is detailed in `DMPlexGetConeOrientation()`.
3453 
3454 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3455 @*/
3456 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3457 {
3458   DM_Plex *mesh = (DM_Plex *)dm->data;
3459   PetscInt pStart, pEnd;
3460   PetscInt dof, off, c;
3461 
3462   PetscFunctionBegin;
3463   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3464   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3465   if (dof) PetscAssertPointer(coneOrientation, 3);
3466   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3467   if (PetscDefined(USE_DEBUG)) {
3468     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3469     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);
3470     for (c = 0; c < dof; ++c) {
3471       PetscInt cdof, o = coneOrientation[c];
3472 
3473       PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3474       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);
3475       mesh->coneOrientations[off + c] = o;
3476     }
3477   } else {
3478     for (c = 0; c < dof; ++c) mesh->coneOrientations[off + c] = coneOrientation[c];
3479   }
3480   PetscFunctionReturn(PETSC_SUCCESS);
3481 }
3482 
3483 /*@
3484   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3485 
3486   Not Collective
3487 
3488   Input Parameters:
3489 + dm        - The `DMPLEX`
3490 . p         - The point, which must lie in the chart set with `DMPlexSetChart()`
3491 . conePos   - The local index in the cone where the point should be put
3492 - conePoint - The mesh point to insert
3493 
3494   Level: beginner
3495 
3496 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3497 @*/
3498 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3499 {
3500   DM_Plex *mesh = (DM_Plex *)dm->data;
3501   PetscInt pStart, pEnd;
3502   PetscInt dof, off;
3503 
3504   PetscFunctionBegin;
3505   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3506   if (PetscDefined(USE_DEBUG)) {
3507     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3508     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);
3509     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);
3510     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3511     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);
3512   }
3513   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3514   mesh->cones[off + conePos] = conePoint;
3515   PetscFunctionReturn(PETSC_SUCCESS);
3516 }
3517 
3518 /*@
3519   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3520 
3521   Not Collective
3522 
3523   Input Parameters:
3524 + dm              - The `DMPLEX`
3525 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3526 . conePos         - The local index in the cone where the point should be put
3527 - coneOrientation - The point orientation to insert
3528 
3529   Level: beginner
3530 
3531   Note:
3532   The meaning of coneOrientation values is detailed in `DMPlexGetConeOrientation()`.
3533 
3534 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3535 @*/
3536 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3537 {
3538   DM_Plex *mesh = (DM_Plex *)dm->data;
3539   PetscInt pStart, pEnd;
3540   PetscInt dof, off;
3541 
3542   PetscFunctionBegin;
3543   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3544   if (PetscDefined(USE_DEBUG)) {
3545     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3546     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);
3547     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3548     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);
3549   }
3550   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3551   mesh->coneOrientations[off + conePos] = coneOrientation;
3552   PetscFunctionReturn(PETSC_SUCCESS);
3553 }
3554 
3555 /*@C
3556   DMPlexGetOrientedCone - Return the points and orientations on the in-edges for this point in the DAG
3557 
3558   Not collective
3559 
3560   Input Parameters:
3561 + dm - The DMPlex
3562 - p  - The point, which must lie in the chart set with DMPlexSetChart()
3563 
3564   Output Parameters:
3565 + cone - An array of points which are on the in-edges for point `p`
3566 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3567          integer giving the prescription for cone traversal.
3568 
3569   Level: beginner
3570 
3571   Notes:
3572   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3573   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3574   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3575   with the identity.
3576 
3577   You must also call `DMPlexRestoreOrientedCone()` after you finish using the returned array.
3578 
3579   Fortran Notes:
3580   `cone` and `ornt` must be declared with
3581 .vb
3582   PetscInt, pointer :: cone(:)
3583   PetscInt, pointer :: ornt(:)
3584 .ve
3585 
3586 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3587 @*/
3588 PetscErrorCode DMPlexGetOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3589 {
3590   DM_Plex *mesh = (DM_Plex *)dm->data;
3591 
3592   PetscFunctionBegin;
3593   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3594   if (mesh->tr) {
3595     PetscCall(DMPlexTransformGetCone(mesh->tr, p, cone, ornt));
3596   } else {
3597     PetscInt off;
3598     if (PetscDefined(USE_DEBUG)) {
3599       PetscInt dof;
3600       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3601       if (dof) {
3602         if (cone) PetscAssertPointer(cone, 3);
3603         if (ornt) PetscAssertPointer(ornt, 4);
3604       }
3605     }
3606     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3607     if (cone) *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3608     if (ornt) *ornt = PetscSafePointerPlusOffset(mesh->coneOrientations, off);
3609   }
3610   PetscFunctionReturn(PETSC_SUCCESS);
3611 }
3612 
3613 /*@C
3614   DMPlexRestoreOrientedCone - Restore the points and orientations on the in-edges for this point in the DAG obtained with `DMPlexGetOrientedCone()`
3615 
3616   Not Collective
3617 
3618   Input Parameters:
3619 + dm   - The DMPlex
3620 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3621 . cone - An array of points which are on the in-edges for point p
3622 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3623          integer giving the prescription for cone traversal.
3624 
3625   Level: beginner
3626 
3627 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3628 @*/
3629 PetscErrorCode DMPlexRestoreOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3630 {
3631   DM_Plex *mesh = (DM_Plex *)dm->data;
3632 
3633   PetscFunctionBegin;
3634   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3635   if (mesh->tr) PetscCall(DMPlexTransformRestoreCone(mesh->tr, p, cone, ornt));
3636   PetscFunctionReturn(PETSC_SUCCESS);
3637 }
3638 
3639 /*@
3640   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3641 
3642   Not Collective
3643 
3644   Input Parameters:
3645 + dm - The `DMPLEX`
3646 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3647 
3648   Output Parameter:
3649 . size - The support size for point `p`
3650 
3651   Level: beginner
3652 
3653 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3654 @*/
3655 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3656 {
3657   DM_Plex *mesh = (DM_Plex *)dm->data;
3658 
3659   PetscFunctionBegin;
3660   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3661   PetscAssertPointer(size, 3);
3662   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3663   PetscFunctionReturn(PETSC_SUCCESS);
3664 }
3665 
3666 /*@
3667   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3668 
3669   Not Collective
3670 
3671   Input Parameters:
3672 + dm   - The `DMPLEX`
3673 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3674 - size - The support size for point `p`
3675 
3676   Level: beginner
3677 
3678   Note:
3679   This should be called after `DMPlexSetChart()`.
3680 
3681 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3682 @*/
3683 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3684 {
3685   DM_Plex *mesh = (DM_Plex *)dm->data;
3686 
3687   PetscFunctionBegin;
3688   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3689   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3690   PetscFunctionReturn(PETSC_SUCCESS);
3691 }
3692 
3693 /*@C
3694   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3695 
3696   Not Collective
3697 
3698   Input Parameters:
3699 + dm - The `DMPLEX`
3700 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3701 
3702   Output Parameter:
3703 . support - An array of points which are on the out-edges for point `p`, its length is that obtained from `DMPlexGetSupportSize()`
3704 
3705   Level: beginner
3706 
3707   Fortran Notes:
3708   `support` must be declared with
3709 .vb
3710   PetscInt, pointer :: support(:)
3711 .ve
3712 
3713   You must also call `DMPlexRestoreSupport()` after you finish using the returned array.
3714   `DMPlexRestoreSupport()` is not needed/available in C.
3715 
3716 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3717 @*/
3718 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3719 {
3720   DM_Plex *mesh = (DM_Plex *)dm->data;
3721   PetscInt off;
3722 
3723   PetscFunctionBegin;
3724   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3725   PetscAssertPointer(support, 3);
3726   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3727   *support = PetscSafePointerPlusOffset(mesh->supports, off);
3728   PetscFunctionReturn(PETSC_SUCCESS);
3729 }
3730 
3731 /*@
3732   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3733 
3734   Not Collective
3735 
3736   Input Parameters:
3737 + dm      - The `DMPLEX`
3738 . p       - The point, which must lie in the chart set with `DMPlexSetChart()`
3739 - support - An array of points which are on the out-edges for point `p`, its length is that obtained from `DMPlexGetSupportSize()`
3740 
3741   Level: beginner
3742 
3743   Note:
3744   This should be called after all calls to `DMPlexSetSupportSize()` and `DMSetUp()`.
3745 
3746 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3747 @*/
3748 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3749 {
3750   DM_Plex *mesh = (DM_Plex *)dm->data;
3751   PetscInt pStart, pEnd;
3752   PetscInt dof, off, c;
3753 
3754   PetscFunctionBegin;
3755   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3756   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3757   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3758   if (dof) PetscAssertPointer(support, 3);
3759   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3760   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);
3761   for (c = 0; c < dof; ++c) {
3762     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);
3763     mesh->supports[off + c] = support[c];
3764   }
3765   PetscFunctionReturn(PETSC_SUCCESS);
3766 }
3767 
3768 /*@
3769   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3770 
3771   Not Collective
3772 
3773   Input Parameters:
3774 + dm           - The `DMPLEX`
3775 . p            - The point, which must lie in the chart set with `DMPlexSetChart()`
3776 . supportPos   - The local index in the cone where the point should be put
3777 - supportPoint - The mesh point to insert
3778 
3779   Level: beginner
3780 
3781 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3782 @*/
3783 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3784 {
3785   DM_Plex *mesh = (DM_Plex *)dm->data;
3786   PetscInt pStart, pEnd;
3787   PetscInt dof, off;
3788 
3789   PetscFunctionBegin;
3790   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3791   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3792   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3793   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3794   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);
3795   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);
3796   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);
3797   mesh->supports[off + supportPos] = supportPoint;
3798   PetscFunctionReturn(PETSC_SUCCESS);
3799 }
3800 
3801 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3802 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3803 {
3804   switch (ct) {
3805   case DM_POLYTOPE_SEGMENT:
3806     if (o == -1) return -2;
3807     break;
3808   case DM_POLYTOPE_TRIANGLE:
3809     if (o == -3) return -1;
3810     if (o == -2) return -3;
3811     if (o == -1) return -2;
3812     break;
3813   case DM_POLYTOPE_QUADRILATERAL:
3814     if (o == -4) return -2;
3815     if (o == -3) return -1;
3816     if (o == -2) return -4;
3817     if (o == -1) return -3;
3818     break;
3819   default:
3820     return o;
3821   }
3822   return o;
3823 }
3824 
3825 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3826 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3827 {
3828   switch (ct) {
3829   case DM_POLYTOPE_SEGMENT:
3830     if ((o == -2) || (o == 1)) return -1;
3831     if (o == -1) return 0;
3832     break;
3833   case DM_POLYTOPE_TRIANGLE:
3834     if (o == -3) return -2;
3835     if (o == -2) return -1;
3836     if (o == -1) return -3;
3837     break;
3838   case DM_POLYTOPE_QUADRILATERAL:
3839     if (o == -4) return -2;
3840     if (o == -3) return -1;
3841     if (o == -2) return -4;
3842     if (o == -1) return -3;
3843     break;
3844   default:
3845     return o;
3846   }
3847   return o;
3848 }
3849 
3850 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3851 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3852 {
3853   PetscInt pStart, pEnd, p;
3854 
3855   PetscFunctionBegin;
3856   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3857   for (p = pStart; p < pEnd; ++p) {
3858     const PetscInt *cone, *ornt;
3859     PetscInt        coneSize, c;
3860 
3861     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3862     PetscCall(DMPlexGetCone(dm, p, &cone));
3863     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3864     for (c = 0; c < coneSize; ++c) {
3865       DMPolytopeType ct;
3866       const PetscInt o = ornt[c];
3867 
3868       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3869       switch (ct) {
3870       case DM_POLYTOPE_SEGMENT:
3871         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3872         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3873         break;
3874       case DM_POLYTOPE_TRIANGLE:
3875         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3876         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3877         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3878         break;
3879       case DM_POLYTOPE_QUADRILATERAL:
3880         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3881         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3882         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3883         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3884         break;
3885       default:
3886         break;
3887       }
3888     }
3889   }
3890   PetscFunctionReturn(PETSC_SUCCESS);
3891 }
3892 
3893 static inline PetscErrorCode DMPlexGetTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3894 {
3895   DM_Plex *mesh = (DM_Plex *)dm->data;
3896 
3897   PetscFunctionBeginHot;
3898   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3899     if (useCone) {
3900       PetscCall(DMPlexGetConeSize(dm, p, size));
3901       PetscCall(DMPlexGetOrientedCone(dm, p, arr, ornt));
3902     } else {
3903       PetscCall(DMPlexGetSupportSize(dm, p, size));
3904       PetscCall(DMPlexGetSupport(dm, p, arr));
3905     }
3906   } else {
3907     if (useCone) {
3908       const PetscSection s   = mesh->coneSection;
3909       const PetscInt     ps  = p - s->pStart;
3910       const PetscInt     off = s->atlasOff[ps];
3911 
3912       *size = s->atlasDof[ps];
3913       *arr  = mesh->cones + off;
3914       *ornt = mesh->coneOrientations + off;
3915     } else {
3916       const PetscSection s   = mesh->supportSection;
3917       const PetscInt     ps  = p - s->pStart;
3918       const PetscInt     off = s->atlasOff[ps];
3919 
3920       *size = s->atlasDof[ps];
3921       *arr  = mesh->supports + off;
3922     }
3923   }
3924   PetscFunctionReturn(PETSC_SUCCESS);
3925 }
3926 
3927 static inline PetscErrorCode DMPlexRestoreTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3928 {
3929   DM_Plex *mesh = (DM_Plex *)dm->data;
3930 
3931   PetscFunctionBeginHot;
3932   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3933     if (useCone) PetscCall(DMPlexRestoreOrientedCone(dm, p, arr, ornt));
3934   }
3935   PetscFunctionReturn(PETSC_SUCCESS);
3936 }
3937 
3938 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3939 {
3940   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3941   PetscInt       *closure;
3942   const PetscInt *tmp = NULL, *tmpO = NULL;
3943   PetscInt        off = 0, tmpSize, t;
3944 
3945   PetscFunctionBeginHot;
3946   if (ornt) {
3947     PetscCall(DMPlexGetCellType(dm, p, &ct));
3948     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;
3949   }
3950   if (*points) {
3951     closure = *points;
3952   } else {
3953     PetscInt maxConeSize, maxSupportSize;
3954     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3955     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
3956   }
3957   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3958   if (ct == DM_POLYTOPE_UNKNOWN) {
3959     closure[off++] = p;
3960     closure[off++] = 0;
3961     for (t = 0; t < tmpSize; ++t) {
3962       closure[off++] = tmp[t];
3963       closure[off++] = tmpO ? tmpO[t] : 0;
3964     }
3965   } else {
3966     const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, ornt);
3967 
3968     /* We assume that cells with a valid type have faces with a valid type */
3969     closure[off++] = p;
3970     closure[off++] = ornt;
3971     for (t = 0; t < tmpSize; ++t) {
3972       DMPolytopeType ft;
3973 
3974       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3975       closure[off++] = tmp[arr[t]];
3976       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3977     }
3978   }
3979   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3980   if (numPoints) *numPoints = tmpSize + 1;
3981   if (points) *points = closure;
3982   PetscFunctionReturn(PETSC_SUCCESS);
3983 }
3984 
3985 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
3986 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3987 {
3988   const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, o);
3989   const PetscInt *cone, *ornt;
3990   PetscInt       *pts, *closure = NULL;
3991   DMPolytopeType  ft;
3992   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3993   PetscInt        dim, coneSize, c, d, clSize, cl;
3994 
3995   PetscFunctionBeginHot;
3996   PetscCall(DMGetDimension(dm, &dim));
3997   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3998   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3999   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
4000   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
4001   maxSize       = PetscMax(coneSeries, supportSeries);
4002   if (*points) {
4003     pts = *points;
4004   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
4005   c        = 0;
4006   pts[c++] = point;
4007   pts[c++] = o;
4008   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
4009   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
4010   for (cl = 0; cl < clSize * 2; cl += 2) {
4011     pts[c++] = closure[cl];
4012     pts[c++] = closure[cl + 1];
4013   }
4014   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
4015   for (cl = 0; cl < clSize * 2; cl += 2) {
4016     pts[c++] = closure[cl];
4017     pts[c++] = closure[cl + 1];
4018   }
4019   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
4020   for (d = 2; d < coneSize; ++d) {
4021     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
4022     pts[c++] = cone[arr[d * 2 + 0]];
4023     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
4024   }
4025   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
4026   if (dim >= 3) {
4027     for (d = 2; d < coneSize; ++d) {
4028       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
4029       const PetscInt *fcone, *fornt;
4030       PetscInt        fconeSize, fc, i;
4031 
4032       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
4033       const PetscInt *farr = DMPolytopeTypeGetArrangement(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
4034       PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
4035       for (fc = 0; fc < fconeSize; ++fc) {
4036         const PetscInt cp = fcone[farr[fc * 2 + 0]];
4037         const PetscInt co = farr[fc * 2 + 1];
4038 
4039         for (i = 0; i < c; i += 2)
4040           if (pts[i] == cp) break;
4041         if (i == c) {
4042           PetscCall(DMPlexGetCellType(dm, cp, &ft));
4043           pts[c++] = cp;
4044           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
4045         }
4046       }
4047       PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
4048     }
4049   }
4050   *numPoints = c / 2;
4051   *points    = pts;
4052   PetscFunctionReturn(PETSC_SUCCESS);
4053 }
4054 
4055 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4056 {
4057   DMPolytopeType ct;
4058   PetscInt      *closure, *fifo;
4059   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
4060   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
4061   PetscInt       depth, maxSize;
4062 
4063   PetscFunctionBeginHot;
4064   PetscCall(DMPlexGetDepth(dm, &depth));
4065   if (depth == 1) {
4066     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
4067     PetscFunctionReturn(PETSC_SUCCESS);
4068   }
4069   PetscCall(DMPlexGetCellType(dm, p, &ct));
4070   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;
4071   if (DMPolytopeTypeIsHybrid(ct) && ct != DM_POLYTOPE_POINT_PRISM_TENSOR) {
4072     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
4073     PetscFunctionReturn(PETSC_SUCCESS);
4074   }
4075   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
4076   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
4077   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
4078   maxSize       = PetscMax(coneSeries, supportSeries);
4079   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
4080   if (*points) {
4081     closure = *points;
4082   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
4083   closure[closureSize++] = p;
4084   closure[closureSize++] = ornt;
4085   fifo[fifoSize++]       = p;
4086   fifo[fifoSize++]       = ornt;
4087   fifo[fifoSize++]       = ct;
4088   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
4089   while (fifoSize - fifoStart) {
4090     const PetscInt       q    = fifo[fifoStart++];
4091     const PetscInt       o    = fifo[fifoStart++];
4092     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
4093     const PetscInt      *qarr = DMPolytopeTypeGetArrangement(qt, o);
4094     const PetscInt      *tmp, *tmpO = NULL;
4095     PetscInt             tmpSize, t;
4096 
4097     if (PetscDefined(USE_DEBUG)) {
4098       PetscInt nO = DMPolytopeTypeGetNumArrangements(qt) / 2;
4099       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);
4100     }
4101     PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4102     for (t = 0; t < tmpSize; ++t) {
4103       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
4104       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
4105       const PetscInt cp = tmp[ip];
4106       PetscCall(DMPlexGetCellType(dm, cp, &ct));
4107       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
4108       PetscInt       c;
4109 
4110       /* Check for duplicate */
4111       for (c = 0; c < closureSize; c += 2) {
4112         if (closure[c] == cp) break;
4113       }
4114       if (c == closureSize) {
4115         closure[closureSize++] = cp;
4116         closure[closureSize++] = co;
4117         fifo[fifoSize++]       = cp;
4118         fifo[fifoSize++]       = co;
4119         fifo[fifoSize++]       = ct;
4120       }
4121     }
4122     PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4123   }
4124   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
4125   if (numPoints) *numPoints = closureSize / 2;
4126   if (points) *points = closure;
4127   PetscFunctionReturn(PETSC_SUCCESS);
4128 }
4129 
4130 /*@C
4131   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
4132 
4133   Not Collective
4134 
4135   Input Parameters:
4136 + dm      - The `DMPLEX`
4137 . p       - The mesh point
4138 - useCone - `PETSC_TRUE` for the closure, otherwise return the star
4139 
4140   Input/Output Parameter:
4141 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
4142            if *points is `NULL` on input, internal storage will be returned, use `DMPlexRestoreTransitiveClosure()`,
4143            otherwise the provided array is used to hold the values
4144 
4145   Output Parameter:
4146 . numPoints - The number of points in the closure, so `points` is of size 2*`numPoints`
4147 
4148   Level: beginner
4149 
4150   Note:
4151   If using internal storage (points is `NULL` on input), each call overwrites the last output.
4152 
4153   Fortran Notes:
4154   `points` must be declared with
4155 .vb
4156   PetscInt, pointer :: points(:)
4157 .ve
4158   and is always allocated by the function.
4159 
4160   The `numPoints` argument is not present in the Fortran binding.
4161 
4162 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4163 @*/
4164 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4165 {
4166   PetscFunctionBeginHot;
4167   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4168   if (numPoints) PetscAssertPointer(numPoints, 4);
4169   if (points) PetscAssertPointer(points, 5);
4170   if (PetscDefined(USE_DEBUG)) {
4171     PetscInt pStart, pEnd;
4172     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4173     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);
4174   }
4175   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
4176   PetscFunctionReturn(PETSC_SUCCESS);
4177 }
4178 
4179 /*@C
4180   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
4181 
4182   Not Collective
4183 
4184   Input Parameters:
4185 + dm        - The `DMPLEX`
4186 . p         - The mesh point
4187 . useCone   - `PETSC_TRUE` for the closure, otherwise return the star
4188 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
4189 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
4190 
4191   Level: beginner
4192 
4193   Note:
4194   If not using internal storage (points is not `NULL` on input), this call is unnecessary
4195 
4196 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4197 @*/
4198 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4199 {
4200   PetscFunctionBeginHot;
4201   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4202   if (numPoints) *numPoints = 0;
4203   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
4204   PetscFunctionReturn(PETSC_SUCCESS);
4205 }
4206 
4207 /*@
4208   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
4209 
4210   Not Collective
4211 
4212   Input Parameter:
4213 . dm - The `DMPLEX`
4214 
4215   Output Parameters:
4216 + maxConeSize    - The maximum number of in-edges
4217 - maxSupportSize - The maximum number of out-edges
4218 
4219   Level: beginner
4220 
4221 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
4222 @*/
4223 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
4224 {
4225   DM_Plex *mesh = (DM_Plex *)dm->data;
4226 
4227   PetscFunctionBegin;
4228   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4229   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
4230   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
4231   PetscFunctionReturn(PETSC_SUCCESS);
4232 }
4233 
4234 PetscErrorCode DMSetUp_Plex(DM dm)
4235 {
4236   DM_Plex *mesh = (DM_Plex *)dm->data;
4237   PetscInt size, maxSupportSize;
4238 
4239   PetscFunctionBegin;
4240   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4241   PetscCall(PetscSectionSetUp(mesh->coneSection));
4242   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
4243   PetscCall(PetscMalloc1(size, &mesh->cones));
4244   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
4245   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4246   if (maxSupportSize) {
4247     PetscCall(PetscSectionSetUp(mesh->supportSection));
4248     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
4249     PetscCall(PetscMalloc1(size, &mesh->supports));
4250   }
4251   PetscFunctionReturn(PETSC_SUCCESS);
4252 }
4253 
4254 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
4255 {
4256   PetscFunctionBegin;
4257   if (subdm) PetscCall(DMClone(dm, subdm));
4258   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, NULL, NULL, is, subdm));
4259   if (subdm) (*subdm)->useNatural = dm->useNatural;
4260   if (dm->useNatural && dm->sfMigration) {
4261     PetscSF sfNatural;
4262 
4263     (*subdm)->sfMigration = dm->sfMigration;
4264     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
4265     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
4266     (*subdm)->sfNatural = sfNatural;
4267   }
4268   PetscFunctionReturn(PETSC_SUCCESS);
4269 }
4270 
4271 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
4272 {
4273   PetscInt i = 0;
4274 
4275   PetscFunctionBegin;
4276   PetscCall(DMClone(dms[0], superdm));
4277   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
4278   (*superdm)->useNatural = PETSC_FALSE;
4279   for (i = 0; i < len; i++) {
4280     if (dms[i]->useNatural && dms[i]->sfMigration) {
4281       PetscSF sfNatural;
4282 
4283       (*superdm)->sfMigration = dms[i]->sfMigration;
4284       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
4285       (*superdm)->useNatural = PETSC_TRUE;
4286       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
4287       (*superdm)->sfNatural = sfNatural;
4288       break;
4289     }
4290   }
4291   PetscFunctionReturn(PETSC_SUCCESS);
4292 }
4293 
4294 /*@
4295   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
4296 
4297   Not Collective
4298 
4299   Input Parameter:
4300 . dm - The `DMPLEX`
4301 
4302   Level: beginner
4303 
4304   Note:
4305   This should be called after all calls to `DMPlexSetCone()`
4306 
4307 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
4308 @*/
4309 PetscErrorCode DMPlexSymmetrize(DM dm)
4310 {
4311   DM_Plex  *mesh = (DM_Plex *)dm->data;
4312   PetscInt *offsets;
4313   PetscInt  supportSize;
4314   PetscInt  pStart, pEnd, p;
4315 
4316   PetscFunctionBegin;
4317   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4318   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
4319   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
4320   /* Calculate support sizes */
4321   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4322   for (p = pStart; p < pEnd; ++p) {
4323     PetscInt dof, off, c;
4324 
4325     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4326     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4327     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
4328   }
4329   PetscCall(PetscSectionSetUp(mesh->supportSection));
4330   /* Calculate supports */
4331   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
4332   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
4333   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
4334   for (p = pStart; p < pEnd; ++p) {
4335     PetscInt dof, off, c;
4336 
4337     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4338     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4339     for (c = off; c < off + dof; ++c) {
4340       const PetscInt q = mesh->cones[c];
4341       PetscInt       offS;
4342 
4343       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
4344 
4345       mesh->supports[offS + offsets[q]] = p;
4346       ++offsets[q];
4347     }
4348   }
4349   PetscCall(PetscFree(offsets));
4350   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
4351   PetscFunctionReturn(PETSC_SUCCESS);
4352 }
4353 
4354 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
4355 {
4356   IS stratumIS;
4357 
4358   PetscFunctionBegin;
4359   if (pStart >= pEnd) PetscFunctionReturn(PETSC_SUCCESS);
4360   if (PetscDefined(USE_DEBUG)) {
4361     PetscInt  qStart, qEnd, numLevels, level;
4362     PetscBool overlap = PETSC_FALSE;
4363     PetscCall(DMLabelGetNumValues(label, &numLevels));
4364     for (level = 0; level < numLevels; level++) {
4365       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4366       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
4367         overlap = PETSC_TRUE;
4368         break;
4369       }
4370     }
4371     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);
4372   }
4373   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
4374   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
4375   PetscCall(ISDestroy(&stratumIS));
4376   PetscFunctionReturn(PETSC_SUCCESS);
4377 }
4378 
4379 static PetscErrorCode DMPlexStratify_CellType_Private(DM dm, DMLabel label)
4380 {
4381   PetscInt *pMin, *pMax;
4382   PetscInt  pStart, pEnd;
4383   PetscInt  dmin = PETSC_MAX_INT, dmax = PETSC_MIN_INT;
4384 
4385   PetscFunctionBegin;
4386   {
4387     DMLabel label2;
4388 
4389     PetscCall(DMPlexGetCellTypeLabel(dm, &label2));
4390     PetscCall(PetscObjectViewFromOptions((PetscObject)label2, NULL, "-ct_view"));
4391   }
4392   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4393   for (PetscInt p = pStart; p < pEnd; ++p) {
4394     DMPolytopeType ct;
4395 
4396     PetscCall(DMPlexGetCellType(dm, p, &ct));
4397     dmin = PetscMin(DMPolytopeTypeGetDim(ct), dmin);
4398     dmax = PetscMax(DMPolytopeTypeGetDim(ct), dmax);
4399   }
4400   PetscCall(PetscMalloc2(dmax + 1, &pMin, dmax + 1, &pMax));
4401   for (PetscInt d = dmin; d <= dmax; ++d) {
4402     pMin[d] = PETSC_MAX_INT;
4403     pMax[d] = PETSC_MIN_INT;
4404   }
4405   for (PetscInt p = pStart; p < pEnd; ++p) {
4406     DMPolytopeType ct;
4407     PetscInt       d;
4408 
4409     PetscCall(DMPlexGetCellType(dm, p, &ct));
4410     d       = DMPolytopeTypeGetDim(ct);
4411     pMin[d] = PetscMin(p, pMin[d]);
4412     pMax[d] = PetscMax(p, pMax[d]);
4413   }
4414   for (PetscInt d = dmin; d <= dmax; ++d) {
4415     if (pMin[d] > pMax[d]) continue;
4416     PetscCall(DMPlexCreateDepthStratum(dm, label, d, pMin[d], pMax[d] + 1));
4417   }
4418   PetscCall(PetscFree2(pMin, pMax));
4419   PetscFunctionReturn(PETSC_SUCCESS);
4420 }
4421 
4422 static PetscErrorCode DMPlexStratify_Topological_Private(DM dm, DMLabel label)
4423 {
4424   PetscInt pStart, pEnd;
4425   PetscInt numRoots = 0, numLeaves = 0;
4426 
4427   PetscFunctionBegin;
4428   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4429   {
4430     /* Initialize roots and count leaves */
4431     PetscInt sMin = PETSC_MAX_INT;
4432     PetscInt sMax = PETSC_MIN_INT;
4433     PetscInt coneSize, supportSize;
4434 
4435     for (PetscInt p = pStart; p < pEnd; ++p) {
4436       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4437       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4438       if (!coneSize && supportSize) {
4439         sMin = PetscMin(p, sMin);
4440         sMax = PetscMax(p, sMax);
4441         ++numRoots;
4442       } else if (!supportSize && coneSize) {
4443         ++numLeaves;
4444       } else if (!supportSize && !coneSize) {
4445         /* Isolated points */
4446         sMin = PetscMin(p, sMin);
4447         sMax = PetscMax(p, sMax);
4448       }
4449     }
4450     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4451   }
4452 
4453   if (numRoots + numLeaves == (pEnd - pStart)) {
4454     PetscInt sMin = PETSC_MAX_INT;
4455     PetscInt sMax = PETSC_MIN_INT;
4456     PetscInt coneSize, supportSize;
4457 
4458     for (PetscInt p = pStart; p < pEnd; ++p) {
4459       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4460       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4461       if (!supportSize && coneSize) {
4462         sMin = PetscMin(p, sMin);
4463         sMax = PetscMax(p, sMax);
4464       }
4465     }
4466     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4467   } else {
4468     PetscInt level = 0;
4469     PetscInt qStart, qEnd;
4470 
4471     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4472     while (qEnd > qStart) {
4473       PetscInt sMin = PETSC_MAX_INT;
4474       PetscInt sMax = PETSC_MIN_INT;
4475 
4476       for (PetscInt q = qStart; q < qEnd; ++q) {
4477         const PetscInt *support;
4478         PetscInt        supportSize;
4479 
4480         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4481         PetscCall(DMPlexGetSupport(dm, q, &support));
4482         for (PetscInt s = 0; s < supportSize; ++s) {
4483           sMin = PetscMin(support[s], sMin);
4484           sMax = PetscMax(support[s], sMax);
4485         }
4486       }
4487       PetscCall(DMLabelGetNumValues(label, &level));
4488       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4489       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4490     }
4491   }
4492   PetscFunctionReturn(PETSC_SUCCESS);
4493 }
4494 
4495 /*@
4496   DMPlexStratify - Computes the strata for all points in the `DMPLEX`
4497 
4498   Collective
4499 
4500   Input Parameter:
4501 . dm - The `DMPLEX`
4502 
4503   Level: beginner
4504 
4505   Notes:
4506   The strata group all points of the same grade, and this function calculates the strata. This
4507   grade can be seen as the height (or depth) of the point in the DAG.
4508 
4509   The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
4510   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram).
4511   Concretely, `DMPlexStratify()` creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
4512   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
4513   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through `DMPlexGetDepthLabel()` or `DMPlexGetDepthStratum()`, or
4514   manually via `DMGetLabel()`.  The height is defined implicitly by height = maxDimension - depth, and can be accessed
4515   via `DMPlexGetHeightStratum()`.  For example, cells have height 0 and faces have height 1.
4516 
4517   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
4518   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
4519   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
4520   to interpolate only that one (e0), so that
4521 .vb
4522   cone(c0) = {e0, v2}
4523   cone(e0) = {v0, v1}
4524 .ve
4525   If `DMPlexStratify()` is run on this mesh, it will give depths
4526 .vb
4527    depth 0 = {v0, v1, v2}
4528    depth 1 = {e0, c0}
4529 .ve
4530   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
4531 
4532   `DMPlexStratify()` should be called after all calls to `DMPlexSymmetrize()`
4533 
4534 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
4535 @*/
4536 PetscErrorCode DMPlexStratify(DM dm)
4537 {
4538   DM_Plex  *mesh = (DM_Plex *)dm->data;
4539   DMLabel   label;
4540   PetscBool flg = PETSC_FALSE;
4541 
4542   PetscFunctionBegin;
4543   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4544   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
4545 
4546   // Create depth label
4547   PetscCall(DMRemoveLabel(dm, "depth", NULL));
4548   PetscCall(DMCreateLabel(dm, "depth"));
4549   PetscCall(DMPlexGetDepthLabel(dm, &label));
4550 
4551   PetscCall(PetscOptionsGetBool(NULL, dm->hdr.prefix, "-dm_plex_stratify_celltype", &flg, NULL));
4552   if (flg) PetscCall(DMPlexStratify_CellType_Private(dm, label));
4553   else PetscCall(DMPlexStratify_Topological_Private(dm, label));
4554 
4555   { /* just in case there is an empty process */
4556     PetscInt numValues, maxValues = 0, v;
4557 
4558     PetscCall(DMLabelGetNumValues(label, &numValues));
4559     PetscCall(MPIU_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4560     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4561   }
4562   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4563   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4564   PetscFunctionReturn(PETSC_SUCCESS);
4565 }
4566 
4567 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4568 {
4569   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4570   PetscInt       dim, depth, pheight, coneSize;
4571 
4572   PetscFunctionBeginHot;
4573   PetscCall(DMGetDimension(dm, &dim));
4574   PetscCall(DMPlexGetDepth(dm, &depth));
4575   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4576   pheight = depth - pdepth;
4577   if (depth <= 1) {
4578     switch (pdepth) {
4579     case 0:
4580       ct = DM_POLYTOPE_POINT;
4581       break;
4582     case 1:
4583       switch (coneSize) {
4584       case 2:
4585         ct = DM_POLYTOPE_SEGMENT;
4586         break;
4587       case 3:
4588         ct = DM_POLYTOPE_TRIANGLE;
4589         break;
4590       case 4:
4591         switch (dim) {
4592         case 2:
4593           ct = DM_POLYTOPE_QUADRILATERAL;
4594           break;
4595         case 3:
4596           ct = DM_POLYTOPE_TETRAHEDRON;
4597           break;
4598         default:
4599           break;
4600         }
4601         break;
4602       case 5:
4603         ct = DM_POLYTOPE_PYRAMID;
4604         break;
4605       case 6:
4606         ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4607         break;
4608       case 8:
4609         ct = DM_POLYTOPE_HEXAHEDRON;
4610         break;
4611       default:
4612         break;
4613       }
4614     }
4615   } else {
4616     if (pdepth == 0) {
4617       ct = DM_POLYTOPE_POINT;
4618     } else if (pheight == 0) {
4619       switch (dim) {
4620       case 1:
4621         switch (coneSize) {
4622         case 2:
4623           ct = DM_POLYTOPE_SEGMENT;
4624           break;
4625         default:
4626           break;
4627         }
4628         break;
4629       case 2:
4630         switch (coneSize) {
4631         case 3:
4632           ct = DM_POLYTOPE_TRIANGLE;
4633           break;
4634         case 4:
4635           ct = DM_POLYTOPE_QUADRILATERAL;
4636           break;
4637         default:
4638           break;
4639         }
4640         break;
4641       case 3:
4642         switch (coneSize) {
4643         case 4:
4644           ct = DM_POLYTOPE_TETRAHEDRON;
4645           break;
4646         case 5: {
4647           const PetscInt *cone;
4648           PetscInt        faceConeSize;
4649 
4650           PetscCall(DMPlexGetCone(dm, p, &cone));
4651           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4652           switch (faceConeSize) {
4653           case 3:
4654             ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4655             break;
4656           case 4:
4657             ct = DM_POLYTOPE_PYRAMID;
4658             break;
4659           }
4660         } break;
4661         case 6:
4662           ct = DM_POLYTOPE_HEXAHEDRON;
4663           break;
4664         default:
4665           break;
4666         }
4667         break;
4668       default:
4669         break;
4670       }
4671     } else if (pheight > 0) {
4672       switch (coneSize) {
4673       case 2:
4674         ct = DM_POLYTOPE_SEGMENT;
4675         break;
4676       case 3:
4677         ct = DM_POLYTOPE_TRIANGLE;
4678         break;
4679       case 4:
4680         ct = DM_POLYTOPE_QUADRILATERAL;
4681         break;
4682       default:
4683         break;
4684       }
4685     }
4686   }
4687   *pt = ct;
4688   PetscFunctionReturn(PETSC_SUCCESS);
4689 }
4690 
4691 /*@
4692   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4693 
4694   Collective
4695 
4696   Input Parameter:
4697 . dm - The `DMPLEX`
4698 
4699   Level: developer
4700 
4701   Note:
4702   This function is normally called automatically when a cell type is requested. It creates an
4703   internal `DMLabel` named "celltype" which can be directly accessed using `DMGetLabel()`. A user may disable
4704   automatic creation by creating the label manually, using `DMCreateLabel`(dm, "celltype").
4705 
4706   `DMPlexComputeCellTypes()` should be called after all calls to `DMPlexSymmetrize()` and `DMPlexStratify()`
4707 
4708 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4709 @*/
4710 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4711 {
4712   DM_Plex *mesh;
4713   DMLabel  ctLabel;
4714   PetscInt pStart, pEnd, p;
4715 
4716   PetscFunctionBegin;
4717   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4718   mesh = (DM_Plex *)dm->data;
4719   PetscCall(DMCreateLabel(dm, "celltype"));
4720   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4721   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4722   PetscCall(PetscFree(mesh->cellTypes));
4723   PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
4724   for (p = pStart; p < pEnd; ++p) {
4725     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4726     PetscInt       pdepth;
4727 
4728     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4729     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4730     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]);
4731     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4732     mesh->cellTypes[p - pStart].value_as_uint8 = ct;
4733   }
4734   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4735   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4736   PetscFunctionReturn(PETSC_SUCCESS);
4737 }
4738 
4739 /*@C
4740   DMPlexGetJoin - Get an array for the join of the set of points
4741 
4742   Not Collective
4743 
4744   Input Parameters:
4745 + dm        - The `DMPLEX` object
4746 . numPoints - The number of input points for the join
4747 - points    - The input points
4748 
4749   Output Parameters:
4750 + numCoveredPoints - The number of points in the join
4751 - coveredPoints    - The points in the join
4752 
4753   Level: intermediate
4754 
4755   Note:
4756   Currently, this is restricted to a single level join
4757 
4758   Fortran Notes:
4759   `converedPoints` must be declared with
4760 .vb
4761   PetscInt, pointer :: coveredPints(:)
4762 .ve
4763 
4764   The `numCoveredPoints` argument is not present in the Fortran binding.
4765 
4766 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4767 @*/
4768 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4769 {
4770   DM_Plex  *mesh = (DM_Plex *)dm->data;
4771   PetscInt *join[2];
4772   PetscInt  joinSize, i = 0;
4773   PetscInt  dof, off, p, c, m;
4774   PetscInt  maxSupportSize;
4775 
4776   PetscFunctionBegin;
4777   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4778   PetscAssertPointer(points, 3);
4779   PetscAssertPointer(numCoveredPoints, 4);
4780   PetscAssertPointer(coveredPoints, 5);
4781   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4782   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4783   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4784   /* Copy in support of first point */
4785   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4786   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4787   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4788   /* Check each successive support */
4789   for (p = 1; p < numPoints; ++p) {
4790     PetscInt newJoinSize = 0;
4791 
4792     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4793     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4794     for (c = 0; c < dof; ++c) {
4795       const PetscInt point = mesh->supports[off + c];
4796 
4797       for (m = 0; m < joinSize; ++m) {
4798         if (point == join[i][m]) {
4799           join[1 - i][newJoinSize++] = point;
4800           break;
4801         }
4802       }
4803     }
4804     joinSize = newJoinSize;
4805     i        = 1 - i;
4806   }
4807   *numCoveredPoints = joinSize;
4808   *coveredPoints    = join[i];
4809   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4810   PetscFunctionReturn(PETSC_SUCCESS);
4811 }
4812 
4813 /*@C
4814   DMPlexRestoreJoin - Restore an array for the join of the set of points obtained with `DMPlexGetJoin()`
4815 
4816   Not Collective
4817 
4818   Input Parameters:
4819 + dm        - The `DMPLEX` object
4820 . numPoints - The number of input points for the join
4821 - points    - The input points
4822 
4823   Output Parameters:
4824 + numCoveredPoints - The number of points in the join
4825 - coveredPoints    - The points in the join
4826 
4827   Level: intermediate
4828 
4829   Fortran Notes:
4830   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4831 
4832 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4833 @*/
4834 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4835 {
4836   PetscFunctionBegin;
4837   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4838   if (points) PetscAssertPointer(points, 3);
4839   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4840   PetscAssertPointer(coveredPoints, 5);
4841   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4842   if (numCoveredPoints) *numCoveredPoints = 0;
4843   PetscFunctionReturn(PETSC_SUCCESS);
4844 }
4845 
4846 /*@C
4847   DMPlexGetFullJoin - Get an array for the join of the set of points
4848 
4849   Not Collective
4850 
4851   Input Parameters:
4852 + dm        - The `DMPLEX` object
4853 . numPoints - The number of input points for the join
4854 - points    - The input points, its length is `numPoints`
4855 
4856   Output Parameters:
4857 + numCoveredPoints - The number of points in the join
4858 - coveredPoints    - The points in the join, its length is `numCoveredPoints`
4859 
4860   Level: intermediate
4861 
4862   Fortran Notes:
4863   `points` and `converedPoints` must be declared with
4864 .vb
4865   PetscInt, pointer :: points(:)
4866   PetscInt, pointer :: coveredPints(:)
4867 .ve
4868 
4869   The `numCoveredPoints` argument is not present in the Fortran binding.
4870 
4871 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4872 @*/
4873 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4874 {
4875   PetscInt *offsets, **closures;
4876   PetscInt *join[2];
4877   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4878   PetscInt  p, d, c, m, ms;
4879 
4880   PetscFunctionBegin;
4881   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4882   PetscAssertPointer(points, 3);
4883   PetscAssertPointer(numCoveredPoints, 4);
4884   PetscAssertPointer(coveredPoints, 5);
4885 
4886   PetscCall(DMPlexGetDepth(dm, &depth));
4887   PetscCall(PetscCalloc1(numPoints, &closures));
4888   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4889   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4890   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4891   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4892   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4893 
4894   for (p = 0; p < numPoints; ++p) {
4895     PetscInt closureSize;
4896 
4897     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4898 
4899     offsets[p * (depth + 2) + 0] = 0;
4900     for (d = 0; d < depth + 1; ++d) {
4901       PetscInt pStart, pEnd, i;
4902 
4903       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4904       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4905         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4906           offsets[p * (depth + 2) + d + 1] = i;
4907           break;
4908         }
4909       }
4910       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4911     }
4912     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);
4913   }
4914   for (d = 0; d < depth + 1; ++d) {
4915     PetscInt dof;
4916 
4917     /* Copy in support of first point */
4918     dof = offsets[d + 1] - offsets[d];
4919     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4920     /* Check each successive cone */
4921     for (p = 1; p < numPoints && joinSize; ++p) {
4922       PetscInt newJoinSize = 0;
4923 
4924       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
4925       for (c = 0; c < dof; ++c) {
4926         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
4927 
4928         for (m = 0; m < joinSize; ++m) {
4929           if (point == join[i][m]) {
4930             join[1 - i][newJoinSize++] = point;
4931             break;
4932           }
4933         }
4934       }
4935       joinSize = newJoinSize;
4936       i        = 1 - i;
4937     }
4938     if (joinSize) break;
4939   }
4940   *numCoveredPoints = joinSize;
4941   *coveredPoints    = join[i];
4942   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4943   PetscCall(PetscFree(closures));
4944   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4945   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
4946   PetscFunctionReturn(PETSC_SUCCESS);
4947 }
4948 
4949 /*@C
4950   DMPlexGetMeet - Get an array for the meet of the set of points
4951 
4952   Not Collective
4953 
4954   Input Parameters:
4955 + dm        - The `DMPLEX` object
4956 . numPoints - The number of input points for the meet
4957 - points    - The input points, of length `numPoints`
4958 
4959   Output Parameters:
4960 + numCoveringPoints - The number of points in the meet
4961 - coveringPoints    - The points in the meet, of length `numCoveringPoints`
4962 
4963   Level: intermediate
4964 
4965   Note:
4966   Currently, this is restricted to a single level meet
4967 
4968   Fortran Notes:
4969   `coveringPoints` must be declared with
4970 .vb
4971   PetscInt, pointer :: coveringPoints(:)
4972 .ve
4973 
4974   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4975 
4976 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4977 @*/
4978 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt *coveringPoints[])
4979 {
4980   DM_Plex  *mesh = (DM_Plex *)dm->data;
4981   PetscInt *meet[2];
4982   PetscInt  meetSize, i = 0;
4983   PetscInt  dof, off, p, c, m;
4984   PetscInt  maxConeSize;
4985 
4986   PetscFunctionBegin;
4987   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4988   PetscAssertPointer(points, 3);
4989   PetscAssertPointer(numCoveringPoints, 4);
4990   PetscAssertPointer(coveringPoints, 5);
4991   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4992   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4993   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4994   /* Copy in cone of first point */
4995   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4996   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4997   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
4998   /* Check each successive cone */
4999   for (p = 1; p < numPoints; ++p) {
5000     PetscInt newMeetSize = 0;
5001 
5002     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
5003     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
5004     for (c = 0; c < dof; ++c) {
5005       const PetscInt point = mesh->cones[off + c];
5006 
5007       for (m = 0; m < meetSize; ++m) {
5008         if (point == meet[i][m]) {
5009           meet[1 - i][newMeetSize++] = point;
5010           break;
5011         }
5012       }
5013     }
5014     meetSize = newMeetSize;
5015     i        = 1 - i;
5016   }
5017   *numCoveringPoints = meetSize;
5018   *coveringPoints    = meet[i];
5019   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
5020   PetscFunctionReturn(PETSC_SUCCESS);
5021 }
5022 
5023 /*@C
5024   DMPlexRestoreMeet - Restore an array for the meet of the set of points obtained with `DMPlexGetMeet()`
5025 
5026   Not Collective
5027 
5028   Input Parameters:
5029 + dm        - The `DMPLEX` object
5030 . numPoints - The number of input points for the meet
5031 - points    - The input points
5032 
5033   Output Parameters:
5034 + numCoveredPoints - The number of points in the meet
5035 - coveredPoints    - The points in the meet
5036 
5037   Level: intermediate
5038 
5039   Fortran Notes:
5040   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
5041 
5042 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
5043 @*/
5044 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
5045 {
5046   PetscFunctionBegin;
5047   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5048   if (points) PetscAssertPointer(points, 3);
5049   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
5050   PetscAssertPointer(coveredPoints, 5);
5051   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
5052   if (numCoveredPoints) *numCoveredPoints = 0;
5053   PetscFunctionReturn(PETSC_SUCCESS);
5054 }
5055 
5056 /*@C
5057   DMPlexGetFullMeet - Get an array for the meet of the set of points
5058 
5059   Not Collective
5060 
5061   Input Parameters:
5062 + dm        - The `DMPLEX` object
5063 . numPoints - The number of input points for the meet
5064 - points    - The input points, of length  `numPoints`
5065 
5066   Output Parameters:
5067 + numCoveredPoints - The number of points in the meet
5068 - coveredPoints    - The points in the meet, of length  `numCoveredPoints`
5069 
5070   Level: intermediate
5071 
5072   Fortran Notes:
5073   `points` and `coveredPoints` must be declared with
5074 .vb
5075   PetscInt, pointer :: points(:)
5076   PetscInt, pointer :: coveredPoints(:)
5077 .ve
5078 
5079   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
5080 
5081 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
5082 @*/
5083 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
5084 {
5085   PetscInt *offsets, **closures;
5086   PetscInt *meet[2];
5087   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
5088   PetscInt  p, h, c, m, mc;
5089 
5090   PetscFunctionBegin;
5091   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5092   PetscAssertPointer(points, 3);
5093   PetscAssertPointer(numCoveredPoints, 4);
5094   PetscAssertPointer(coveredPoints, 5);
5095 
5096   PetscCall(DMPlexGetDepth(dm, &height));
5097   PetscCall(PetscMalloc1(numPoints, &closures));
5098   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5099   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
5100   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
5101   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
5102   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
5103 
5104   for (p = 0; p < numPoints; ++p) {
5105     PetscInt closureSize;
5106 
5107     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
5108 
5109     offsets[p * (height + 2) + 0] = 0;
5110     for (h = 0; h < height + 1; ++h) {
5111       PetscInt pStart, pEnd, i;
5112 
5113       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
5114       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
5115         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
5116           offsets[p * (height + 2) + h + 1] = i;
5117           break;
5118         }
5119       }
5120       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
5121     }
5122     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);
5123   }
5124   for (h = 0; h < height + 1; ++h) {
5125     PetscInt dof;
5126 
5127     /* Copy in cone of first point */
5128     dof = offsets[h + 1] - offsets[h];
5129     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
5130     /* Check each successive cone */
5131     for (p = 1; p < numPoints && meetSize; ++p) {
5132       PetscInt newMeetSize = 0;
5133 
5134       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
5135       for (c = 0; c < dof; ++c) {
5136         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
5137 
5138         for (m = 0; m < meetSize; ++m) {
5139           if (point == meet[i][m]) {
5140             meet[1 - i][newMeetSize++] = point;
5141             break;
5142           }
5143         }
5144       }
5145       meetSize = newMeetSize;
5146       i        = 1 - i;
5147     }
5148     if (meetSize) break;
5149   }
5150   *numCoveredPoints = meetSize;
5151   *coveredPoints    = meet[i];
5152   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
5153   PetscCall(PetscFree(closures));
5154   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5155   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
5156   PetscFunctionReturn(PETSC_SUCCESS);
5157 }
5158 
5159 /*@
5160   DMPlexEqual - Determine if two `DM` have the same topology
5161 
5162   Not Collective
5163 
5164   Input Parameters:
5165 + dmA - A `DMPLEX` object
5166 - dmB - A `DMPLEX` object
5167 
5168   Output Parameter:
5169 . equal - `PETSC_TRUE` if the topologies are identical
5170 
5171   Level: intermediate
5172 
5173   Note:
5174   We are not solving graph isomorphism, so we do not permute.
5175 
5176 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5177 @*/
5178 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
5179 {
5180   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
5181 
5182   PetscFunctionBegin;
5183   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
5184   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
5185   PetscAssertPointer(equal, 3);
5186 
5187   *equal = PETSC_FALSE;
5188   PetscCall(DMPlexGetDepth(dmA, &depth));
5189   PetscCall(DMPlexGetDepth(dmB, &depthB));
5190   if (depth != depthB) PetscFunctionReturn(PETSC_SUCCESS);
5191   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
5192   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
5193   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(PETSC_SUCCESS);
5194   for (p = pStart; p < pEnd; ++p) {
5195     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
5196     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
5197 
5198     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
5199     PetscCall(DMPlexGetCone(dmA, p, &cone));
5200     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
5201     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
5202     PetscCall(DMPlexGetCone(dmB, p, &coneB));
5203     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
5204     if (coneSize != coneSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5205     for (c = 0; c < coneSize; ++c) {
5206       if (cone[c] != coneB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5207       if (ornt[c] != orntB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5208     }
5209     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
5210     PetscCall(DMPlexGetSupport(dmA, p, &support));
5211     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
5212     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
5213     if (supportSize != supportSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5214     for (s = 0; s < supportSize; ++s) {
5215       if (support[s] != supportB[s]) PetscFunctionReturn(PETSC_SUCCESS);
5216     }
5217   }
5218   *equal = PETSC_TRUE;
5219   PetscFunctionReturn(PETSC_SUCCESS);
5220 }
5221 
5222 /*@
5223   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
5224 
5225   Not Collective
5226 
5227   Input Parameters:
5228 + dm         - The `DMPLEX`
5229 . cellDim    - The cell dimension
5230 - numCorners - The number of vertices on a cell
5231 
5232   Output Parameter:
5233 . numFaceVertices - The number of vertices on a face
5234 
5235   Level: developer
5236 
5237   Note:
5238   Of course this can only work for a restricted set of symmetric shapes
5239 
5240 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5241 @*/
5242 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
5243 {
5244   MPI_Comm comm;
5245 
5246   PetscFunctionBegin;
5247   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
5248   PetscAssertPointer(numFaceVertices, 4);
5249   switch (cellDim) {
5250   case 0:
5251     *numFaceVertices = 0;
5252     break;
5253   case 1:
5254     *numFaceVertices = 1;
5255     break;
5256   case 2:
5257     switch (numCorners) {
5258     case 3:                 /* triangle */
5259       *numFaceVertices = 2; /* Edge has 2 vertices */
5260       break;
5261     case 4:                 /* quadrilateral */
5262       *numFaceVertices = 2; /* Edge has 2 vertices */
5263       break;
5264     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
5265       *numFaceVertices = 3; /* Edge has 3 vertices */
5266       break;
5267     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
5268       *numFaceVertices = 3; /* Edge has 3 vertices */
5269       break;
5270     default:
5271       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5272     }
5273     break;
5274   case 3:
5275     switch (numCorners) {
5276     case 4:                 /* tetradehdron */
5277       *numFaceVertices = 3; /* Face has 3 vertices */
5278       break;
5279     case 6:                 /* tet cohesive cells */
5280       *numFaceVertices = 4; /* Face has 4 vertices */
5281       break;
5282     case 8:                 /* hexahedron */
5283       *numFaceVertices = 4; /* Face has 4 vertices */
5284       break;
5285     case 9:                 /* tet cohesive Lagrange cells */
5286       *numFaceVertices = 6; /* Face has 6 vertices */
5287       break;
5288     case 10:                /* quadratic tetrahedron */
5289       *numFaceVertices = 6; /* Face has 6 vertices */
5290       break;
5291     case 12:                /* hex cohesive Lagrange cells */
5292       *numFaceVertices = 6; /* Face has 6 vertices */
5293       break;
5294     case 18:                /* quadratic tet cohesive Lagrange cells */
5295       *numFaceVertices = 6; /* Face has 6 vertices */
5296       break;
5297     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
5298       *numFaceVertices = 9; /* Face has 9 vertices */
5299       break;
5300     default:
5301       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5302     }
5303     break;
5304   default:
5305     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
5306   }
5307   PetscFunctionReturn(PETSC_SUCCESS);
5308 }
5309 
5310 /*@
5311   DMPlexGetDepthLabel - Get the `DMLabel` recording the depth of each point
5312 
5313   Not Collective
5314 
5315   Input Parameter:
5316 . dm - The `DMPLEX` object
5317 
5318   Output Parameter:
5319 . depthLabel - The `DMLabel` recording point depth
5320 
5321   Level: developer
5322 
5323 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
5324 @*/
5325 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
5326 {
5327   PetscFunctionBegin;
5328   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5329   PetscAssertPointer(depthLabel, 2);
5330   *depthLabel = dm->depthLabel;
5331   PetscFunctionReturn(PETSC_SUCCESS);
5332 }
5333 
5334 /*@
5335   DMPlexGetDepth - Get the depth of the DAG representing this mesh
5336 
5337   Not Collective
5338 
5339   Input Parameter:
5340 . dm - The `DMPLEX` object
5341 
5342   Output Parameter:
5343 . depth - The number of strata (breadth first levels) in the DAG
5344 
5345   Level: developer
5346 
5347   Notes:
5348   This returns maximum of point depths over all points, i.e. maximum value of the label returned by `DMPlexGetDepthLabel()`.
5349 
5350   The point depth is described more in detail in `DMPlexGetDepthStratum()`.
5351 
5352   An empty mesh gives -1.
5353 
5354 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
5355 @*/
5356 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
5357 {
5358   DM_Plex *mesh = (DM_Plex *)dm->data;
5359   DMLabel  label;
5360   PetscInt d = -1;
5361 
5362   PetscFunctionBegin;
5363   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5364   PetscAssertPointer(depth, 2);
5365   if (mesh->tr) {
5366     PetscCall(DMPlexTransformGetDepth(mesh->tr, depth));
5367   } else {
5368     PetscCall(DMPlexGetDepthLabel(dm, &label));
5369     // Allow missing depths
5370     if (label) PetscCall(DMLabelGetValueBounds(label, NULL, &d));
5371     *depth = d;
5372   }
5373   PetscFunctionReturn(PETSC_SUCCESS);
5374 }
5375 
5376 /*@
5377   DMPlexGetDepthStratum - Get the bounds [`start`, `end`) for all points at a certain depth.
5378 
5379   Not Collective
5380 
5381   Input Parameters:
5382 + dm    - The `DMPLEX` object
5383 - depth - The requested depth
5384 
5385   Output Parameters:
5386 + start - The first point at this `depth`
5387 - end   - One beyond the last point at this `depth`
5388 
5389   Level: developer
5390 
5391   Notes:
5392   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
5393   often "vertices".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then depth stratum 1 contains the next
5394   higher dimension, e.g., "edges".
5395 
5396 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetHeightStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
5397 @*/
5398 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
5399 {
5400   DM_Plex *mesh = (DM_Plex *)dm->data;
5401   DMLabel  label;
5402   PetscInt pStart, pEnd;
5403 
5404   PetscFunctionBegin;
5405   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5406   if (start) {
5407     PetscAssertPointer(start, 3);
5408     *start = 0;
5409   }
5410   if (end) {
5411     PetscAssertPointer(end, 4);
5412     *end = 0;
5413   }
5414   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5415   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5416   if (depth < 0) {
5417     if (start) *start = pStart;
5418     if (end) *end = pEnd;
5419     PetscFunctionReturn(PETSC_SUCCESS);
5420   }
5421   if (mesh->tr) {
5422     PetscCall(DMPlexTransformGetDepthStratum(mesh->tr, depth, start, end));
5423   } else {
5424     PetscCall(DMPlexGetDepthLabel(dm, &label));
5425     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5426     PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
5427   }
5428   PetscFunctionReturn(PETSC_SUCCESS);
5429 }
5430 
5431 /*@
5432   DMPlexGetHeightStratum - Get the bounds [`start`, `end`) for all points at a certain height.
5433 
5434   Not Collective
5435 
5436   Input Parameters:
5437 + dm     - The `DMPLEX` object
5438 - height - The requested height
5439 
5440   Output Parameters:
5441 + start - The first point at this `height`
5442 - end   - One beyond the last point at this `height`
5443 
5444   Level: developer
5445 
5446   Notes:
5447   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
5448   points, often called "cells" or "elements".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then height
5449   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
5450 
5451 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5452 @*/
5453 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
5454 {
5455   DMLabel  label;
5456   PetscInt depth, pStart, pEnd;
5457 
5458   PetscFunctionBegin;
5459   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5460   if (start) {
5461     PetscAssertPointer(start, 3);
5462     *start = 0;
5463   }
5464   if (end) {
5465     PetscAssertPointer(end, 4);
5466     *end = 0;
5467   }
5468   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5469   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5470   if (height < 0) {
5471     if (start) *start = pStart;
5472     if (end) *end = pEnd;
5473     PetscFunctionReturn(PETSC_SUCCESS);
5474   }
5475   PetscCall(DMPlexGetDepthLabel(dm, &label));
5476   if (label) PetscCall(DMLabelGetNumValues(label, &depth));
5477   else PetscCall(DMGetDimension(dm, &depth));
5478   PetscCheck(depth >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Depth not yet computed");
5479   PetscCall(DMPlexGetDepthStratum(dm, depth - 1 - height, start, end));
5480   PetscFunctionReturn(PETSC_SUCCESS);
5481 }
5482 
5483 /*@
5484   DMPlexGetPointDepth - Get the `depth` of a given point
5485 
5486   Not Collective
5487 
5488   Input Parameters:
5489 + dm    - The `DMPLEX` object
5490 - point - The point
5491 
5492   Output Parameter:
5493 . depth - The depth of the `point`
5494 
5495   Level: intermediate
5496 
5497 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5498 @*/
5499 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
5500 {
5501   PetscFunctionBegin;
5502   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5503   PetscAssertPointer(depth, 3);
5504   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
5505   PetscFunctionReturn(PETSC_SUCCESS);
5506 }
5507 
5508 /*@
5509   DMPlexGetPointHeight - Get the `height` of a given point
5510 
5511   Not Collective
5512 
5513   Input Parameters:
5514 + dm    - The `DMPLEX` object
5515 - point - The point
5516 
5517   Output Parameter:
5518 . height - The height of the `point`
5519 
5520   Level: intermediate
5521 
5522 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
5523 @*/
5524 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5525 {
5526   PetscInt n, pDepth;
5527 
5528   PetscFunctionBegin;
5529   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5530   PetscAssertPointer(height, 3);
5531   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5532   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5533   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5534   PetscFunctionReturn(PETSC_SUCCESS);
5535 }
5536 
5537 /*@
5538   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5539 
5540   Not Collective
5541 
5542   Input Parameter:
5543 . dm - The `DMPLEX` object
5544 
5545   Output Parameter:
5546 . celltypeLabel - The `DMLabel` recording cell polytope type
5547 
5548   Level: developer
5549 
5550   Note:
5551   This function will trigger automatica computation of cell types. This can be disabled by calling
5552   `DMCreateLabel`(dm, "celltype") beforehand.
5553 
5554 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5555 @*/
5556 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5557 {
5558   PetscFunctionBegin;
5559   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5560   PetscAssertPointer(celltypeLabel, 2);
5561   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5562   *celltypeLabel = dm->celltypeLabel;
5563   PetscFunctionReturn(PETSC_SUCCESS);
5564 }
5565 
5566 /*@
5567   DMPlexGetCellType - Get the polytope type of a given cell
5568 
5569   Not Collective
5570 
5571   Input Parameters:
5572 + dm   - The `DMPLEX` object
5573 - cell - The cell
5574 
5575   Output Parameter:
5576 . celltype - The polytope type of the cell
5577 
5578   Level: intermediate
5579 
5580 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeType`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5581 @*/
5582 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5583 {
5584   DM_Plex *mesh = (DM_Plex *)dm->data;
5585   DMLabel  label;
5586   PetscInt ct;
5587 
5588   PetscFunctionBegin;
5589   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5590   PetscAssertPointer(celltype, 3);
5591   if (mesh->tr) {
5592     PetscCall(DMPlexTransformGetCellType(mesh->tr, cell, celltype));
5593   } else {
5594     PetscInt pStart, pEnd;
5595 
5596     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, NULL));
5597     if (!mesh->cellTypes) { /* XXX remove? optimize? */
5598       PetscCall(PetscSectionGetChart(mesh->coneSection, NULL, &pEnd));
5599       PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5600       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5601       for (PetscInt p = pStart; p < pEnd; p++) {
5602         PetscCall(DMLabelGetValue(label, p, &ct));
5603         mesh->cellTypes[p - pStart].value_as_uint8 = (DMPolytopeType)ct;
5604       }
5605     }
5606     *celltype = (DMPolytopeType)mesh->cellTypes[cell - pStart].value_as_uint8;
5607     if (PetscDefined(USE_DEBUG)) {
5608       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5609       PetscCall(DMLabelGetValue(label, cell, &ct));
5610       PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5611       PetscCheck(ct == (PetscInt)*celltype, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid cellType for %" PetscInt_FMT ": %d != %" PetscInt_FMT, cell, (int)*celltype, ct);
5612     }
5613   }
5614   PetscFunctionReturn(PETSC_SUCCESS);
5615 }
5616 
5617 /*@
5618   DMPlexSetCellType - Set the polytope type of a given cell
5619 
5620   Not Collective
5621 
5622   Input Parameters:
5623 + dm       - The `DMPLEX` object
5624 . cell     - The cell
5625 - celltype - The polytope type of the cell
5626 
5627   Level: advanced
5628 
5629   Note:
5630   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5631   is executed. This function will override the computed type. However, if automatic classification will not succeed
5632   and a user wants to manually specify all types, the classification must be disabled by calling
5633   DMCreateLabel(dm, "celltype") before getting or setting any cell types.
5634 
5635 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5636 @*/
5637 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5638 {
5639   DM_Plex *mesh = (DM_Plex *)dm->data;
5640   DMLabel  label;
5641   PetscInt pStart, pEnd;
5642 
5643   PetscFunctionBegin;
5644   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5645   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
5646   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5647   PetscCall(DMLabelSetValue(label, cell, celltype));
5648   if (!mesh->cellTypes) PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5649   mesh->cellTypes[cell - pStart].value_as_uint8 = celltype;
5650   PetscFunctionReturn(PETSC_SUCCESS);
5651 }
5652 
5653 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5654 {
5655   PetscSection section;
5656   PetscInt     maxHeight;
5657   const char  *prefix;
5658 
5659   PetscFunctionBegin;
5660   PetscCall(DMClone(dm, cdm));
5661   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5662   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5663   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5664   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5665   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5666   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5667   PetscCall(DMSetLocalSection(*cdm, section));
5668   PetscCall(PetscSectionDestroy(&section));
5669 
5670   PetscCall(DMSetNumFields(*cdm, 1));
5671   PetscCall(DMCreateDS(*cdm));
5672   (*cdm)->cloneOpts = PETSC_TRUE;
5673   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5674   PetscFunctionReturn(PETSC_SUCCESS);
5675 }
5676 
5677 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5678 {
5679   Vec coordsLocal, cellCoordsLocal;
5680   DM  coordsDM, cellCoordsDM;
5681 
5682   PetscFunctionBegin;
5683   *field = NULL;
5684   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5685   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5686   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5687   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5688   if (coordsLocal && coordsDM) {
5689     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5690     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5691   }
5692   PetscFunctionReturn(PETSC_SUCCESS);
5693 }
5694 
5695 /*@
5696   DMPlexGetConeSection - Return a section which describes the layout of cone data
5697 
5698   Not Collective
5699 
5700   Input Parameter:
5701 . dm - The `DMPLEX` object
5702 
5703   Output Parameter:
5704 . section - The `PetscSection` object
5705 
5706   Level: developer
5707 
5708 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5709 @*/
5710 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5711 {
5712   DM_Plex *mesh = (DM_Plex *)dm->data;
5713 
5714   PetscFunctionBegin;
5715   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5716   if (section) *section = mesh->coneSection;
5717   PetscFunctionReturn(PETSC_SUCCESS);
5718 }
5719 
5720 /*@
5721   DMPlexGetSupportSection - Return a section which describes the layout of support data
5722 
5723   Not Collective
5724 
5725   Input Parameter:
5726 . dm - The `DMPLEX` object
5727 
5728   Output Parameter:
5729 . section - The `PetscSection` object
5730 
5731   Level: developer
5732 
5733 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5734 @*/
5735 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5736 {
5737   DM_Plex *mesh = (DM_Plex *)dm->data;
5738 
5739   PetscFunctionBegin;
5740   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5741   if (section) *section = mesh->supportSection;
5742   PetscFunctionReturn(PETSC_SUCCESS);
5743 }
5744 
5745 /*@C
5746   DMPlexGetCones - Return cone data
5747 
5748   Not Collective
5749 
5750   Input Parameter:
5751 . dm - The `DMPLEX` object
5752 
5753   Output Parameter:
5754 . cones - The cone for each point
5755 
5756   Level: developer
5757 
5758 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5759 @*/
5760 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5761 {
5762   DM_Plex *mesh = (DM_Plex *)dm->data;
5763 
5764   PetscFunctionBegin;
5765   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5766   if (cones) *cones = mesh->cones;
5767   PetscFunctionReturn(PETSC_SUCCESS);
5768 }
5769 
5770 /*@C
5771   DMPlexGetConeOrientations - Return cone orientation data
5772 
5773   Not Collective
5774 
5775   Input Parameter:
5776 . dm - The `DMPLEX` object
5777 
5778   Output Parameter:
5779 . coneOrientations - The array of cone orientations for all points
5780 
5781   Level: developer
5782 
5783   Notes:
5784   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points
5785   as returned by `DMPlexGetConeOrientation()`.
5786 
5787   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5788 
5789 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5790 @*/
5791 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5792 {
5793   DM_Plex *mesh = (DM_Plex *)dm->data;
5794 
5795   PetscFunctionBegin;
5796   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5797   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5798   PetscFunctionReturn(PETSC_SUCCESS);
5799 }
5800 
5801 /* FEM Support */
5802 
5803 PetscErrorCode DMPlexGetAllCells_Internal(DM plex, IS *cellIS)
5804 {
5805   PetscInt depth;
5806 
5807   PetscFunctionBegin;
5808   PetscCall(DMPlexGetDepth(plex, &depth));
5809   PetscCall(DMGetStratumIS(plex, "dim", depth, cellIS));
5810   if (!*cellIS) PetscCall(DMGetStratumIS(plex, "depth", depth, cellIS));
5811   PetscFunctionReturn(PETSC_SUCCESS);
5812 }
5813 
5814 PetscErrorCode DMPlexGetAllFaces_Internal(DM plex, IS *faceIS)
5815 {
5816   PetscInt depth;
5817 
5818   PetscFunctionBegin;
5819   PetscCall(DMPlexGetDepth(plex, &depth));
5820   PetscCall(DMGetStratumIS(plex, "dim", depth - 1, faceIS));
5821   if (!*faceIS) PetscCall(DMGetStratumIS(plex, "depth", depth - 1, faceIS));
5822   PetscFunctionReturn(PETSC_SUCCESS);
5823 }
5824 
5825 /*
5826  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5827  representing a line in the section.
5828 */
5829 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(DM dm, PetscSection section, PetscInt field, PetscInt line, PetscInt *Nc, PetscInt *k, PetscBool *continuous, PetscBool *tensor)
5830 {
5831   PetscObject  obj;
5832   PetscClassId id;
5833   PetscFE      fe = NULL;
5834 
5835   PetscFunctionBeginHot;
5836   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5837   PetscCall(DMGetField(dm, field, NULL, &obj));
5838   PetscCall(PetscObjectGetClassId(obj, &id));
5839   if (id == PETSCFE_CLASSID) fe = (PetscFE)obj;
5840 
5841   if (!fe) {
5842     /* Assume the full interpolated mesh is in the chart; lines in particular */
5843     /* An order k SEM disc has k-1 dofs on an edge */
5844     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5845     *k = *k / *Nc + 1;
5846   } else {
5847     PetscInt       dual_space_size, dim;
5848     PetscDualSpace dsp;
5849 
5850     PetscCall(DMGetDimension(dm, &dim));
5851     PetscCall(PetscFEGetDualSpace(fe, &dsp));
5852     PetscCall(PetscDualSpaceGetDimension(dsp, &dual_space_size));
5853     *k = (PetscInt)PetscCeilReal(PetscPowReal(dual_space_size / *Nc, 1.0 / dim)) - 1;
5854     PetscCall(PetscDualSpaceLagrangeGetContinuity(dsp, continuous));
5855     PetscCall(PetscDualSpaceLagrangeGetTensor(dsp, tensor));
5856   }
5857   PetscFunctionReturn(PETSC_SUCCESS);
5858 }
5859 
5860 static PetscErrorCode GetFieldSize_Private(PetscInt dim, PetscInt k, PetscBool tensor, PetscInt *dof)
5861 {
5862   PetscFunctionBeginHot;
5863   if (tensor) {
5864     *dof = PetscPowInt(k + 1, dim);
5865   } else {
5866     switch (dim) {
5867     case 1:
5868       *dof = k + 1;
5869       break;
5870     case 2:
5871       *dof = ((k + 1) * (k + 2)) / 2;
5872       break;
5873     case 3:
5874       *dof = ((k + 1) * (k + 2) * (k + 3)) / 6;
5875       break;
5876     default:
5877       *dof = 0;
5878     }
5879   }
5880   PetscFunctionReturn(PETSC_SUCCESS);
5881 }
5882 
5883 /*@
5884   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5885   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5886   section provided (or the section of the `DM`).
5887 
5888   Input Parameters:
5889 + dm      - The `DM`
5890 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or `PETSC_DETERMINE`
5891 - section - The `PetscSection` to reorder, or `NULL` for the default section
5892 
5893   Example:
5894   A typical interpolated single-quad mesh might order points as
5895 .vb
5896   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5897 
5898   v4 -- e6 -- v3
5899   |           |
5900   e7    c0    e8
5901   |           |
5902   v1 -- e5 -- v2
5903 .ve
5904 
5905   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5906   dofs in the order of points, e.g.,
5907 .vb
5908     c0 -> [0,1,2,3]
5909     v1 -> [4]
5910     ...
5911     e5 -> [8, 9]
5912 .ve
5913 
5914   which corresponds to the dofs
5915 .vb
5916     6   10  11  7
5917     13  2   3   15
5918     12  0   1   14
5919     4   8   9   5
5920 .ve
5921 
5922   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5923 .vb
5924   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5925 .ve
5926 
5927   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5928 .vb
5929    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5930 .ve
5931 
5932   Level: developer
5933 
5934   Notes:
5935   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5936   degree of the basis.
5937 
5938   This is required to run with libCEED.
5939 
5940 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5941 @*/
5942 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5943 {
5944   DMLabel   label;
5945   PetscInt  dim, depth = -1, eStart = -1, Nf;
5946   PetscBool continuous = PETSC_TRUE, tensor = PETSC_TRUE;
5947 
5948   PetscFunctionBegin;
5949   PetscCall(DMGetDimension(dm, &dim));
5950   if (dim < 1) PetscFunctionReturn(PETSC_SUCCESS);
5951   if (point < 0) {
5952     PetscInt sStart, sEnd;
5953 
5954     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5955     point = sEnd - sStart ? sStart : point;
5956   }
5957   PetscCall(DMPlexGetDepthLabel(dm, &label));
5958   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5959   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5960   if (depth == 1) {
5961     eStart = point;
5962   } else if (depth == dim) {
5963     const PetscInt *cone;
5964 
5965     PetscCall(DMPlexGetCone(dm, point, &cone));
5966     if (dim == 2) eStart = cone[0];
5967     else if (dim == 3) {
5968       const PetscInt *cone2;
5969       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5970       eStart = cone2[0];
5971     } 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);
5972   } 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);
5973 
5974   PetscCall(PetscSectionGetNumFields(section, &Nf));
5975   for (PetscInt d = 1; d <= dim; d++) {
5976     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5977     PetscInt *perm;
5978 
5979     for (f = 0; f < Nf; ++f) {
5980       PetscInt dof;
5981 
5982       PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5983       PetscCheck(dim == 1 || tensor || !continuous, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Continuous field %" PetscInt_FMT " must have a tensor product discretization", f);
5984       if (!continuous && d < dim) continue;
5985       PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
5986       size += dof * Nc;
5987     }
5988     PetscCall(PetscMalloc1(size, &perm));
5989     for (f = 0; f < Nf; ++f) {
5990       switch (d) {
5991       case 1:
5992         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5993         if (!continuous && d < dim) continue;
5994         /*
5995          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5996          We want              [ vtx0; edge of length k-1; vtx1 ]
5997          */
5998         if (continuous) {
5999           for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
6000           for (i = 0; i < k - 1; i++)
6001             for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
6002           for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
6003           foffset = offset;
6004         } else {
6005           PetscInt dof;
6006 
6007           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6008           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6009           foffset = offset;
6010         }
6011         break;
6012       case 2:
6013         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
6014         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6015         if (!continuous && d < dim) continue;
6016         /* The SEM order is
6017 
6018          v_lb, {e_b}, v_rb,
6019          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
6020          v_lt, reverse {e_t}, v_rt
6021          */
6022         if (continuous) {
6023           const PetscInt of   = 0;
6024           const PetscInt oeb  = of + PetscSqr(k - 1);
6025           const PetscInt oer  = oeb + (k - 1);
6026           const PetscInt oet  = oer + (k - 1);
6027           const PetscInt oel  = oet + (k - 1);
6028           const PetscInt ovlb = oel + (k - 1);
6029           const PetscInt ovrb = ovlb + 1;
6030           const PetscInt ovrt = ovrb + 1;
6031           const PetscInt ovlt = ovrt + 1;
6032           PetscInt       o;
6033 
6034           /* bottom */
6035           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
6036           for (o = oeb; o < oer; ++o)
6037             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6038           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
6039           /* middle */
6040           for (i = 0; i < k - 1; ++i) {
6041             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
6042             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
6043               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6044             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
6045           }
6046           /* top */
6047           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
6048           for (o = oel - 1; o >= oet; --o)
6049             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6050           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
6051           foffset = offset;
6052         } else {
6053           PetscInt dof;
6054 
6055           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6056           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6057           foffset = offset;
6058         }
6059         break;
6060       case 3:
6061         /* The original hex closure is
6062 
6063          {c,
6064          f_b, f_t, f_f, f_b, f_r, f_l,
6065          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
6066          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
6067          */
6068         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6069         if (!continuous && d < dim) continue;
6070         /* The SEM order is
6071          Bottom Slice
6072          v_blf, {e^{(k-1)-n}_bf}, v_brf,
6073          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
6074          v_blb, {e_bb}, v_brb,
6075 
6076          Middle Slice (j)
6077          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
6078          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
6079          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
6080 
6081          Top Slice
6082          v_tlf, {e_tf}, v_trf,
6083          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
6084          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
6085          */
6086         if (continuous) {
6087           const PetscInt oc    = 0;
6088           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
6089           const PetscInt oft   = ofb + PetscSqr(k - 1);
6090           const PetscInt off   = oft + PetscSqr(k - 1);
6091           const PetscInt ofk   = off + PetscSqr(k - 1);
6092           const PetscInt ofr   = ofk + PetscSqr(k - 1);
6093           const PetscInt ofl   = ofr + PetscSqr(k - 1);
6094           const PetscInt oebl  = ofl + PetscSqr(k - 1);
6095           const PetscInt oebb  = oebl + (k - 1);
6096           const PetscInt oebr  = oebb + (k - 1);
6097           const PetscInt oebf  = oebr + (k - 1);
6098           const PetscInt oetf  = oebf + (k - 1);
6099           const PetscInt oetr  = oetf + (k - 1);
6100           const PetscInt oetb  = oetr + (k - 1);
6101           const PetscInt oetl  = oetb + (k - 1);
6102           const PetscInt oerf  = oetl + (k - 1);
6103           const PetscInt oelf  = oerf + (k - 1);
6104           const PetscInt oelb  = oelf + (k - 1);
6105           const PetscInt oerb  = oelb + (k - 1);
6106           const PetscInt ovblf = oerb + (k - 1);
6107           const PetscInt ovblb = ovblf + 1;
6108           const PetscInt ovbrb = ovblb + 1;
6109           const PetscInt ovbrf = ovbrb + 1;
6110           const PetscInt ovtlf = ovbrf + 1;
6111           const PetscInt ovtrf = ovtlf + 1;
6112           const PetscInt ovtrb = ovtrf + 1;
6113           const PetscInt ovtlb = ovtrb + 1;
6114           PetscInt       o, n;
6115 
6116           /* Bottom Slice */
6117           /*   bottom */
6118           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
6119           for (o = oetf - 1; o >= oebf; --o)
6120             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6121           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
6122           /*   middle */
6123           for (i = 0; i < k - 1; ++i) {
6124             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
6125             for (n = 0; n < k - 1; ++n) {
6126               o = ofb + n * (k - 1) + i;
6127               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6128             }
6129             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
6130           }
6131           /*   top */
6132           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
6133           for (o = oebb; o < oebr; ++o)
6134             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6135           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
6136 
6137           /* Middle Slice */
6138           for (j = 0; j < k - 1; ++j) {
6139             /*   bottom */
6140             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
6141             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
6142               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6143             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
6144             /*   middle */
6145             for (i = 0; i < k - 1; ++i) {
6146               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
6147               for (n = 0; n < k - 1; ++n)
6148                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
6149               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
6150             }
6151             /*   top */
6152             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
6153             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
6154               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6155             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
6156           }
6157 
6158           /* Top Slice */
6159           /*   bottom */
6160           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
6161           for (o = oetf; o < oetr; ++o)
6162             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6163           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
6164           /*   middle */
6165           for (i = 0; i < k - 1; ++i) {
6166             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
6167             for (n = 0; n < k - 1; ++n)
6168               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
6169             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
6170           }
6171           /*   top */
6172           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
6173           for (o = oetl - 1; o >= oetb; --o)
6174             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6175           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
6176 
6177           foffset = offset;
6178         } else {
6179           PetscInt dof;
6180 
6181           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6182           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6183           foffset = offset;
6184         }
6185         break;
6186       default:
6187         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
6188       }
6189     }
6190     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
6191     /* Check permutation */
6192     {
6193       PetscInt *check;
6194 
6195       PetscCall(PetscMalloc1(size, &check));
6196       for (i = 0; i < size; ++i) {
6197         check[i] = -1;
6198         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
6199       }
6200       for (i = 0; i < size; ++i) check[perm[i]] = i;
6201       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
6202       PetscCall(PetscFree(check));
6203     }
6204     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
6205     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
6206       PetscInt *loc_perm;
6207       PetscCall(PetscMalloc1(size * 2, &loc_perm));
6208       for (PetscInt i = 0; i < size; i++) {
6209         loc_perm[i]        = perm[i];
6210         loc_perm[size + i] = size + perm[i];
6211       }
6212       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
6213     }
6214   }
6215   PetscFunctionReturn(PETSC_SUCCESS);
6216 }
6217 
6218 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
6219 {
6220   PetscDS  prob;
6221   PetscInt depth, Nf, h;
6222   DMLabel  label;
6223 
6224   PetscFunctionBeginHot;
6225   PetscCall(DMGetDS(dm, &prob));
6226   Nf      = prob->Nf;
6227   label   = dm->depthLabel;
6228   *dspace = NULL;
6229   if (field < Nf) {
6230     PetscObject disc = prob->disc[field];
6231 
6232     if (disc->classid == PETSCFE_CLASSID) {
6233       PetscDualSpace dsp;
6234 
6235       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
6236       PetscCall(DMLabelGetNumValues(label, &depth));
6237       PetscCall(DMLabelGetValue(label, point, &h));
6238       h = depth - 1 - h;
6239       if (h) {
6240         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
6241       } else {
6242         *dspace = dsp;
6243       }
6244     }
6245   }
6246   PetscFunctionReturn(PETSC_SUCCESS);
6247 }
6248 
6249 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6250 {
6251   PetscScalar       *array;
6252   const PetscScalar *vArray;
6253   const PetscInt    *cone, *coneO;
6254   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
6255 
6256   PetscFunctionBeginHot;
6257   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6258   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6259   PetscCall(DMPlexGetCone(dm, point, &cone));
6260   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6261   if (!values || !*values) {
6262     if ((point >= pStart) && (point < pEnd)) {
6263       PetscInt dof;
6264 
6265       PetscCall(PetscSectionGetDof(section, point, &dof));
6266       size += dof;
6267     }
6268     for (p = 0; p < numPoints; ++p) {
6269       const PetscInt cp = cone[p];
6270       PetscInt       dof;
6271 
6272       if ((cp < pStart) || (cp >= pEnd)) continue;
6273       PetscCall(PetscSectionGetDof(section, cp, &dof));
6274       size += dof;
6275     }
6276     if (!values) {
6277       if (csize) *csize = size;
6278       PetscFunctionReturn(PETSC_SUCCESS);
6279     }
6280     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
6281   } else {
6282     array = *values;
6283   }
6284   size = 0;
6285   PetscCall(VecGetArrayRead(v, &vArray));
6286   if ((point >= pStart) && (point < pEnd)) {
6287     PetscInt           dof, off, d;
6288     const PetscScalar *varr;
6289 
6290     PetscCall(PetscSectionGetDof(section, point, &dof));
6291     PetscCall(PetscSectionGetOffset(section, point, &off));
6292     varr = PetscSafePointerPlusOffset(vArray, off);
6293     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6294     size += dof;
6295   }
6296   for (p = 0; p < numPoints; ++p) {
6297     const PetscInt     cp = cone[p];
6298     PetscInt           o  = coneO[p];
6299     PetscInt           dof, off, d;
6300     const PetscScalar *varr;
6301 
6302     if ((cp < pStart) || (cp >= pEnd)) continue;
6303     PetscCall(PetscSectionGetDof(section, cp, &dof));
6304     PetscCall(PetscSectionGetOffset(section, cp, &off));
6305     varr = PetscSafePointerPlusOffset(vArray, off);
6306     if (o >= 0) {
6307       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6308     } else {
6309       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
6310     }
6311     size += dof;
6312   }
6313   PetscCall(VecRestoreArrayRead(v, &vArray));
6314   if (!*values) {
6315     if (csize) *csize = size;
6316     *values = array;
6317   } else {
6318     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6319     *csize = size;
6320   }
6321   PetscFunctionReturn(PETSC_SUCCESS);
6322 }
6323 
6324 /* Compress out points not in the section */
6325 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
6326 {
6327   const PetscInt np = *numPoints;
6328   PetscInt       pStart, pEnd, p, q;
6329 
6330   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6331   for (p = 0, q = 0; p < np; ++p) {
6332     const PetscInt r = points[p * 2];
6333     if ((r >= pStart) && (r < pEnd)) {
6334       points[q * 2]     = r;
6335       points[q * 2 + 1] = points[p * 2 + 1];
6336       ++q;
6337     }
6338   }
6339   *numPoints = q;
6340   return PETSC_SUCCESS;
6341 }
6342 
6343 /* Compressed closure does not apply closure permutation */
6344 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt ornt, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6345 {
6346   const PetscInt *cla = NULL;
6347   PetscInt        np, *pts = NULL;
6348 
6349   PetscFunctionBeginHot;
6350   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
6351   if (!ornt && *clPoints) {
6352     PetscInt dof, off;
6353 
6354     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
6355     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
6356     PetscCall(ISGetIndices(*clPoints, &cla));
6357     np  = dof / 2;
6358     pts = PetscSafePointerPlusOffset((PetscInt *)cla, off);
6359   } else {
6360     PetscCall(DMPlexGetTransitiveClosure_Internal(dm, point, ornt, PETSC_TRUE, &np, &pts));
6361     PetscCall(CompressPoints_Private(section, &np, pts));
6362   }
6363   *numPoints = np;
6364   *points    = pts;
6365   *clp       = cla;
6366   PetscFunctionReturn(PETSC_SUCCESS);
6367 }
6368 
6369 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6370 {
6371   PetscFunctionBeginHot;
6372   if (!*clPoints) {
6373     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
6374   } else {
6375     PetscCall(ISRestoreIndices(*clPoints, clp));
6376   }
6377   *numPoints = 0;
6378   *points    = NULL;
6379   *clSec     = NULL;
6380   *clPoints  = NULL;
6381   *clp       = NULL;
6382   PetscFunctionReturn(PETSC_SUCCESS);
6383 }
6384 
6385 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
6386 {
6387   PetscInt            offset = 0, p;
6388   const PetscInt    **perms  = NULL;
6389   const PetscScalar **flips  = NULL;
6390 
6391   PetscFunctionBeginHot;
6392   *size = 0;
6393   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6394   for (p = 0; p < numPoints; p++) {
6395     const PetscInt     point = points[2 * p];
6396     const PetscInt    *perm  = perms ? perms[p] : NULL;
6397     const PetscScalar *flip  = flips ? flips[p] : NULL;
6398     PetscInt           dof, off, d;
6399     const PetscScalar *varr;
6400 
6401     PetscCall(PetscSectionGetDof(section, point, &dof));
6402     PetscCall(PetscSectionGetOffset(section, point, &off));
6403     varr = PetscSafePointerPlusOffset(vArray, off);
6404     if (clperm) {
6405       if (perm) {
6406         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
6407       } else {
6408         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
6409       }
6410       if (flip) {
6411         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
6412       }
6413     } else {
6414       if (perm) {
6415         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
6416       } else {
6417         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
6418       }
6419       if (flip) {
6420         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
6421       }
6422     }
6423     offset += dof;
6424   }
6425   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6426   *size = offset;
6427   PetscFunctionReturn(PETSC_SUCCESS);
6428 }
6429 
6430 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[])
6431 {
6432   PetscInt offset = 0, f;
6433 
6434   PetscFunctionBeginHot;
6435   *size = 0;
6436   for (f = 0; f < numFields; ++f) {
6437     PetscInt            p;
6438     const PetscInt    **perms = NULL;
6439     const PetscScalar **flips = NULL;
6440 
6441     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6442     for (p = 0; p < numPoints; p++) {
6443       const PetscInt     point = points[2 * p];
6444       PetscInt           fdof, foff, b;
6445       const PetscScalar *varr;
6446       const PetscInt    *perm = perms ? perms[p] : NULL;
6447       const PetscScalar *flip = flips ? flips[p] : NULL;
6448 
6449       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6450       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6451       varr = &vArray[foff];
6452       if (clperm) {
6453         if (perm) {
6454           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
6455         } else {
6456           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
6457         }
6458         if (flip) {
6459           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
6460         }
6461       } else {
6462         if (perm) {
6463           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
6464         } else {
6465           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
6466         }
6467         if (flip) {
6468           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
6469         }
6470       }
6471       offset += fdof;
6472     }
6473     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6474   }
6475   *size = offset;
6476   PetscFunctionReturn(PETSC_SUCCESS);
6477 }
6478 
6479 PetscErrorCode DMPlexVecGetOrientedClosure_Internal(DM dm, PetscSection section, PetscBool useClPerm, Vec v, PetscInt point, PetscInt ornt, PetscInt *csize, PetscScalar *values[])
6480 {
6481   PetscSection    clSection;
6482   IS              clPoints;
6483   PetscInt       *points = NULL;
6484   const PetscInt *clp, *perm = NULL;
6485   PetscInt        depth, numFields, numPoints, asize;
6486 
6487   PetscFunctionBeginHot;
6488   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6489   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6490   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6491   PetscValidHeaderSpecific(v, VEC_CLASSID, 4);
6492   PetscCall(DMPlexGetDepth(dm, &depth));
6493   PetscCall(PetscSectionGetNumFields(section, &numFields));
6494   if (depth == 1 && numFields < 2) {
6495     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6496     PetscFunctionReturn(PETSC_SUCCESS);
6497   }
6498   /* Get points */
6499   PetscCall(DMPlexGetCompressedClosure(dm, section, point, ornt, &numPoints, &points, &clSection, &clPoints, &clp));
6500   /* Get sizes */
6501   asize = 0;
6502   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6503     PetscInt dof;
6504     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6505     asize += dof;
6506   }
6507   if (values) {
6508     const PetscScalar *vArray;
6509     PetscInt           size;
6510 
6511     if (*values) {
6512       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);
6513     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6514     if (useClPerm) PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6515     PetscCall(VecGetArrayRead(v, &vArray));
6516     /* Get values */
6517     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6518     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6519     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6520     /* Cleanup array */
6521     PetscCall(VecRestoreArrayRead(v, &vArray));
6522   }
6523   if (csize) *csize = asize;
6524   /* Cleanup points */
6525   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6526   PetscFunctionReturn(PETSC_SUCCESS);
6527 }
6528 
6529 /*@C
6530   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
6531 
6532   Not collective
6533 
6534   Input Parameters:
6535 + dm      - The `DM`
6536 . section - The section describing the layout in `v`, or `NULL` to use the default section
6537 . v       - The local vector
6538 - point   - The point in the `DM`
6539 
6540   Input/Output Parameters:
6541 + csize  - The size of the input values array, or `NULL`; on output the number of values in the closure
6542 - values - An array to use for the values, or *values = `NULL` to have it allocated automatically;
6543            if the user provided `NULL`, it is a borrowed array and should not be freed, use  `DMPlexVecRestoreClosure()` to return it
6544 
6545   Level: intermediate
6546 
6547   Notes:
6548   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6549   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
6550   assembly function, and a user may already have allocated storage for this operation.
6551 
6552   A typical use could be
6553 .vb
6554    values = NULL;
6555    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6556    for (cl = 0; cl < clSize; ++cl) {
6557      <Compute on closure>
6558    }
6559    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
6560 .ve
6561   or
6562 .vb
6563    PetscMalloc1(clMaxSize, &values);
6564    for (p = pStart; p < pEnd; ++p) {
6565      clSize = clMaxSize;
6566      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6567      for (cl = 0; cl < clSize; ++cl) {
6568        <Compute on closure>
6569      }
6570    }
6571    PetscFree(values);
6572 .ve
6573 
6574   Fortran Notes:
6575   The `csize` argument is not present in the Fortran binding.
6576 
6577   `values` must be declared with
6578 .vb
6579   PetscScalar,dimension(:),pointer   :: values
6580 .ve
6581   and it will be allocated internally by PETSc to hold the values returned
6582 
6583 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6584 @*/
6585 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6586 {
6587   PetscFunctionBeginHot;
6588   PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, section, PETSC_TRUE, v, point, 0, csize, values));
6589   PetscFunctionReturn(PETSC_SUCCESS);
6590 }
6591 
6592 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6593 {
6594   DMLabel            depthLabel;
6595   PetscSection       clSection;
6596   IS                 clPoints;
6597   PetscScalar       *array;
6598   const PetscScalar *vArray;
6599   PetscInt          *points = NULL;
6600   const PetscInt    *clp, *perm = NULL;
6601   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6602 
6603   PetscFunctionBeginHot;
6604   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6605   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6606   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6607   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6608   PetscCall(DMPlexGetDepth(dm, &mdepth));
6609   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6610   PetscCall(PetscSectionGetNumFields(section, &numFields));
6611   if (mdepth == 1 && numFields < 2) {
6612     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6613     PetscFunctionReturn(PETSC_SUCCESS);
6614   }
6615   /* Get points */
6616   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6617   for (clsize = 0, p = 0; p < Np; p++) {
6618     PetscInt dof;
6619     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6620     clsize += dof;
6621   }
6622   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6623   /* Filter points */
6624   for (p = 0; p < numPoints * 2; p += 2) {
6625     PetscInt dep;
6626 
6627     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6628     if (dep != depth) continue;
6629     points[Np * 2 + 0] = points[p];
6630     points[Np * 2 + 1] = points[p + 1];
6631     ++Np;
6632   }
6633   /* Get array */
6634   if (!values || !*values) {
6635     PetscInt asize = 0, dof;
6636 
6637     for (p = 0; p < Np * 2; p += 2) {
6638       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6639       asize += dof;
6640     }
6641     if (!values) {
6642       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6643       if (csize) *csize = asize;
6644       PetscFunctionReturn(PETSC_SUCCESS);
6645     }
6646     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6647   } else {
6648     array = *values;
6649   }
6650   PetscCall(VecGetArrayRead(v, &vArray));
6651   /* Get values */
6652   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6653   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6654   /* Cleanup points */
6655   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6656   /* Cleanup array */
6657   PetscCall(VecRestoreArrayRead(v, &vArray));
6658   if (!*values) {
6659     if (csize) *csize = size;
6660     *values = array;
6661   } else {
6662     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6663     *csize = size;
6664   }
6665   PetscFunctionReturn(PETSC_SUCCESS);
6666 }
6667 
6668 /*@C
6669   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point' obtained with `DMPlexVecGetClosure()`
6670 
6671   Not collective
6672 
6673   Input Parameters:
6674 + dm      - The `DM`
6675 . section - The section describing the layout in `v`, or `NULL` to use the default section
6676 . v       - The local vector
6677 . point   - The point in the `DM`
6678 . csize   - The number of values in the closure, or `NULL`
6679 - values  - The array of values
6680 
6681   Level: intermediate
6682 
6683   Note:
6684   The array values are discarded and not copied back into `v`. In order to copy values back to `v`, use `DMPlexVecSetClosure()`
6685 
6686   Fortran Note:
6687   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6688 
6689 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6690 @*/
6691 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6692 {
6693   PetscInt size = 0;
6694 
6695   PetscFunctionBegin;
6696   /* Should work without recalculating size */
6697   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6698   *values = NULL;
6699   PetscFunctionReturn(PETSC_SUCCESS);
6700 }
6701 
6702 static inline void add(PetscScalar *x, PetscScalar y)
6703 {
6704   *x += y;
6705 }
6706 static inline void insert(PetscScalar *x, PetscScalar y)
6707 {
6708   *x = y;
6709 }
6710 
6711 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[])
6712 {
6713   PetscInt        cdof;  /* The number of constraints on this point */
6714   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6715   PetscScalar    *a;
6716   PetscInt        off, cind = 0, k;
6717 
6718   PetscFunctionBegin;
6719   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6720   PetscCall(PetscSectionGetOffset(section, point, &off));
6721   a = &array[off];
6722   if (!cdof || setBC) {
6723     if (clperm) {
6724       if (perm) {
6725         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6726       } else {
6727         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6728       }
6729     } else {
6730       if (perm) {
6731         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6732       } else {
6733         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6734       }
6735     }
6736   } else {
6737     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6738     if (clperm) {
6739       if (perm) {
6740         for (k = 0; k < dof; ++k) {
6741           if ((cind < cdof) && (k == cdofs[cind])) {
6742             ++cind;
6743             continue;
6744           }
6745           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6746         }
6747       } else {
6748         for (k = 0; k < dof; ++k) {
6749           if ((cind < cdof) && (k == cdofs[cind])) {
6750             ++cind;
6751             continue;
6752           }
6753           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6754         }
6755       }
6756     } else {
6757       if (perm) {
6758         for (k = 0; k < dof; ++k) {
6759           if ((cind < cdof) && (k == cdofs[cind])) {
6760             ++cind;
6761             continue;
6762           }
6763           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6764         }
6765       } else {
6766         for (k = 0; k < dof; ++k) {
6767           if ((cind < cdof) && (k == cdofs[cind])) {
6768             ++cind;
6769             continue;
6770           }
6771           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6772         }
6773       }
6774     }
6775   }
6776   PetscFunctionReturn(PETSC_SUCCESS);
6777 }
6778 
6779 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[])
6780 {
6781   PetscInt        cdof;  /* The number of constraints on this point */
6782   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6783   PetscScalar    *a;
6784   PetscInt        off, cind = 0, k;
6785 
6786   PetscFunctionBegin;
6787   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6788   PetscCall(PetscSectionGetOffset(section, point, &off));
6789   a = &array[off];
6790   if (cdof) {
6791     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6792     if (clperm) {
6793       if (perm) {
6794         for (k = 0; k < dof; ++k) {
6795           if ((cind < cdof) && (k == cdofs[cind])) {
6796             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6797             cind++;
6798           }
6799         }
6800       } else {
6801         for (k = 0; k < dof; ++k) {
6802           if ((cind < cdof) && (k == cdofs[cind])) {
6803             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6804             cind++;
6805           }
6806         }
6807       }
6808     } else {
6809       if (perm) {
6810         for (k = 0; k < dof; ++k) {
6811           if ((cind < cdof) && (k == cdofs[cind])) {
6812             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6813             cind++;
6814           }
6815         }
6816       } else {
6817         for (k = 0; k < dof; ++k) {
6818           if ((cind < cdof) && (k == cdofs[cind])) {
6819             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6820             cind++;
6821           }
6822         }
6823       }
6824     }
6825   }
6826   PetscFunctionReturn(PETSC_SUCCESS);
6827 }
6828 
6829 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[])
6830 {
6831   PetscScalar    *a;
6832   PetscInt        fdof, foff, fcdof, foffset = *offset;
6833   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6834   PetscInt        cind = 0, b;
6835 
6836   PetscFunctionBegin;
6837   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6838   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6839   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6840   a = &array[foff];
6841   if (!fcdof || setBC) {
6842     if (clperm) {
6843       if (perm) {
6844         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6845       } else {
6846         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6847       }
6848     } else {
6849       if (perm) {
6850         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6851       } else {
6852         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6853       }
6854     }
6855   } else {
6856     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6857     if (clperm) {
6858       if (perm) {
6859         for (b = 0; b < fdof; b++) {
6860           if ((cind < fcdof) && (b == fcdofs[cind])) {
6861             ++cind;
6862             continue;
6863           }
6864           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6865         }
6866       } else {
6867         for (b = 0; b < fdof; b++) {
6868           if ((cind < fcdof) && (b == fcdofs[cind])) {
6869             ++cind;
6870             continue;
6871           }
6872           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6873         }
6874       }
6875     } else {
6876       if (perm) {
6877         for (b = 0; b < fdof; b++) {
6878           if ((cind < fcdof) && (b == fcdofs[cind])) {
6879             ++cind;
6880             continue;
6881           }
6882           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6883         }
6884       } else {
6885         for (b = 0; b < fdof; b++) {
6886           if ((cind < fcdof) && (b == fcdofs[cind])) {
6887             ++cind;
6888             continue;
6889           }
6890           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6891         }
6892       }
6893     }
6894   }
6895   *offset += fdof;
6896   PetscFunctionReturn(PETSC_SUCCESS);
6897 }
6898 
6899 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[])
6900 {
6901   PetscScalar    *a;
6902   PetscInt        fdof, foff, fcdof, foffset = *offset;
6903   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6904   PetscInt        Nc, cind = 0, ncind = 0, b;
6905   PetscBool       ncSet, fcSet;
6906 
6907   PetscFunctionBegin;
6908   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6909   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6910   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6911   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6912   a = &array[foff];
6913   if (fcdof) {
6914     /* We just override fcdof and fcdofs with Ncc and comps */
6915     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6916     if (clperm) {
6917       if (perm) {
6918         if (comps) {
6919           for (b = 0; b < fdof; b++) {
6920             ncSet = fcSet = PETSC_FALSE;
6921             if (b % Nc == comps[ncind]) {
6922               ncind = (ncind + 1) % Ncc;
6923               ncSet = PETSC_TRUE;
6924             }
6925             if ((cind < fcdof) && (b == fcdofs[cind])) {
6926               ++cind;
6927               fcSet = PETSC_TRUE;
6928             }
6929             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6930           }
6931         } else {
6932           for (b = 0; b < fdof; b++) {
6933             if ((cind < fcdof) && (b == fcdofs[cind])) {
6934               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6935               ++cind;
6936             }
6937           }
6938         }
6939       } else {
6940         if (comps) {
6941           for (b = 0; b < fdof; b++) {
6942             ncSet = fcSet = PETSC_FALSE;
6943             if (b % Nc == comps[ncind]) {
6944               ncind = (ncind + 1) % Ncc;
6945               ncSet = PETSC_TRUE;
6946             }
6947             if ((cind < fcdof) && (b == fcdofs[cind])) {
6948               ++cind;
6949               fcSet = PETSC_TRUE;
6950             }
6951             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6952           }
6953         } else {
6954           for (b = 0; b < fdof; b++) {
6955             if ((cind < fcdof) && (b == fcdofs[cind])) {
6956               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6957               ++cind;
6958             }
6959           }
6960         }
6961       }
6962     } else {
6963       if (perm) {
6964         if (comps) {
6965           for (b = 0; b < fdof; b++) {
6966             ncSet = fcSet = PETSC_FALSE;
6967             if (b % Nc == comps[ncind]) {
6968               ncind = (ncind + 1) % Ncc;
6969               ncSet = PETSC_TRUE;
6970             }
6971             if ((cind < fcdof) && (b == fcdofs[cind])) {
6972               ++cind;
6973               fcSet = PETSC_TRUE;
6974             }
6975             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6976           }
6977         } else {
6978           for (b = 0; b < fdof; b++) {
6979             if ((cind < fcdof) && (b == fcdofs[cind])) {
6980               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6981               ++cind;
6982             }
6983           }
6984         }
6985       } else {
6986         if (comps) {
6987           for (b = 0; b < fdof; b++) {
6988             ncSet = fcSet = PETSC_FALSE;
6989             if (b % Nc == comps[ncind]) {
6990               ncind = (ncind + 1) % Ncc;
6991               ncSet = PETSC_TRUE;
6992             }
6993             if ((cind < fcdof) && (b == fcdofs[cind])) {
6994               ++cind;
6995               fcSet = PETSC_TRUE;
6996             }
6997             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6998           }
6999         } else {
7000           for (b = 0; b < fdof; b++) {
7001             if ((cind < fcdof) && (b == fcdofs[cind])) {
7002               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
7003               ++cind;
7004             }
7005           }
7006         }
7007       }
7008     }
7009   }
7010   *offset += fdof;
7011   PetscFunctionReturn(PETSC_SUCCESS);
7012 }
7013 
7014 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
7015 {
7016   PetscScalar    *array;
7017   const PetscInt *cone, *coneO;
7018   PetscInt        pStart, pEnd, p, numPoints, off, dof;
7019 
7020   PetscFunctionBeginHot;
7021   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
7022   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
7023   PetscCall(DMPlexGetCone(dm, point, &cone));
7024   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
7025   PetscCall(VecGetArray(v, &array));
7026   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
7027     const PetscInt cp = !p ? point : cone[p - 1];
7028     const PetscInt o  = !p ? 0 : coneO[p - 1];
7029 
7030     if ((cp < pStart) || (cp >= pEnd)) {
7031       dof = 0;
7032       continue;
7033     }
7034     PetscCall(PetscSectionGetDof(section, cp, &dof));
7035     /* ADD_VALUES */
7036     {
7037       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7038       PetscScalar    *a;
7039       PetscInt        cdof, coff, cind = 0, k;
7040 
7041       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
7042       PetscCall(PetscSectionGetOffset(section, cp, &coff));
7043       a = &array[coff];
7044       if (!cdof) {
7045         if (o >= 0) {
7046           for (k = 0; k < dof; ++k) a[k] += values[off + k];
7047         } else {
7048           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
7049         }
7050       } else {
7051         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
7052         if (o >= 0) {
7053           for (k = 0; k < dof; ++k) {
7054             if ((cind < cdof) && (k == cdofs[cind])) {
7055               ++cind;
7056               continue;
7057             }
7058             a[k] += values[off + k];
7059           }
7060         } else {
7061           for (k = 0; k < dof; ++k) {
7062             if ((cind < cdof) && (k == cdofs[cind])) {
7063               ++cind;
7064               continue;
7065             }
7066             a[k] += values[off + dof - k - 1];
7067           }
7068         }
7069       }
7070     }
7071   }
7072   PetscCall(VecRestoreArray(v, &array));
7073   PetscFunctionReturn(PETSC_SUCCESS);
7074 }
7075 
7076 /*@C
7077   DMPlexVecSetClosure - Set an array of the values on the closure of `point`
7078 
7079   Not collective
7080 
7081   Input Parameters:
7082 + dm      - The `DM`
7083 . section - The section describing the layout in `v`, or `NULL` to use the default section
7084 . v       - The local vector
7085 . point   - The point in the `DM`
7086 . values  - The array of values
7087 - mode    - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
7088             where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
7089 
7090   Level: intermediate
7091 
7092   Note:
7093   Usually the input arrays were obtained with `DMPlexVecGetClosure()`
7094 
7095   Fortran Note:
7096   `values` must be declared with
7097 .vb
7098   PetscScalar,dimension(:),pointer   :: values
7099 .ve
7100 
7101 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
7102 @*/
7103 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
7104 {
7105   PetscSection    clSection;
7106   IS              clPoints;
7107   PetscScalar    *array;
7108   PetscInt       *points = NULL;
7109   const PetscInt *clp, *clperm = NULL;
7110   PetscInt        depth, numFields, numPoints, p, clsize;
7111 
7112   PetscFunctionBeginHot;
7113   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7114   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7115   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7116   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7117   PetscCall(DMPlexGetDepth(dm, &depth));
7118   PetscCall(PetscSectionGetNumFields(section, &numFields));
7119   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
7120     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
7121     PetscFunctionReturn(PETSC_SUCCESS);
7122   }
7123   /* Get points */
7124   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7125   for (clsize = 0, p = 0; p < numPoints; p++) {
7126     PetscInt dof;
7127     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7128     clsize += dof;
7129   }
7130   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7131   /* Get array */
7132   PetscCall(VecGetArray(v, &array));
7133   /* Get values */
7134   if (numFields > 0) {
7135     PetscInt offset = 0, f;
7136     for (f = 0; f < numFields; ++f) {
7137       const PetscInt    **perms = NULL;
7138       const PetscScalar **flips = NULL;
7139 
7140       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7141       switch (mode) {
7142       case INSERT_VALUES:
7143         for (p = 0; p < numPoints; p++) {
7144           const PetscInt     point = points[2 * p];
7145           const PetscInt    *perm  = perms ? perms[p] : NULL;
7146           const PetscScalar *flip  = flips ? flips[p] : NULL;
7147           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array));
7148         }
7149         break;
7150       case INSERT_ALL_VALUES:
7151         for (p = 0; p < numPoints; p++) {
7152           const PetscInt     point = points[2 * p];
7153           const PetscInt    *perm  = perms ? perms[p] : NULL;
7154           const PetscScalar *flip  = flips ? flips[p] : NULL;
7155           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array));
7156         }
7157         break;
7158       case INSERT_BC_VALUES:
7159         for (p = 0; p < numPoints; p++) {
7160           const PetscInt     point = points[2 * p];
7161           const PetscInt    *perm  = perms ? perms[p] : NULL;
7162           const PetscScalar *flip  = flips ? flips[p] : NULL;
7163           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array));
7164         }
7165         break;
7166       case ADD_VALUES:
7167         for (p = 0; p < numPoints; p++) {
7168           const PetscInt     point = points[2 * p];
7169           const PetscInt    *perm  = perms ? perms[p] : NULL;
7170           const PetscScalar *flip  = flips ? flips[p] : NULL;
7171           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array));
7172         }
7173         break;
7174       case ADD_ALL_VALUES:
7175         for (p = 0; p < numPoints; p++) {
7176           const PetscInt     point = points[2 * p];
7177           const PetscInt    *perm  = perms ? perms[p] : NULL;
7178           const PetscScalar *flip  = flips ? flips[p] : NULL;
7179           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array));
7180         }
7181         break;
7182       case ADD_BC_VALUES:
7183         for (p = 0; p < numPoints; p++) {
7184           const PetscInt     point = points[2 * p];
7185           const PetscInt    *perm  = perms ? perms[p] : NULL;
7186           const PetscScalar *flip  = flips ? flips[p] : NULL;
7187           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array));
7188         }
7189         break;
7190       default:
7191         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7192       }
7193       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7194     }
7195   } else {
7196     PetscInt            dof, off;
7197     const PetscInt    **perms = NULL;
7198     const PetscScalar **flips = NULL;
7199 
7200     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
7201     switch (mode) {
7202     case INSERT_VALUES:
7203       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7204         const PetscInt     point = points[2 * p];
7205         const PetscInt    *perm  = perms ? perms[p] : NULL;
7206         const PetscScalar *flip  = flips ? flips[p] : NULL;
7207         PetscCall(PetscSectionGetDof(section, point, &dof));
7208         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array));
7209       }
7210       break;
7211     case INSERT_ALL_VALUES:
7212       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7213         const PetscInt     point = points[2 * p];
7214         const PetscInt    *perm  = perms ? perms[p] : NULL;
7215         const PetscScalar *flip  = flips ? flips[p] : NULL;
7216         PetscCall(PetscSectionGetDof(section, point, &dof));
7217         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array));
7218       }
7219       break;
7220     case INSERT_BC_VALUES:
7221       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7222         const PetscInt     point = points[2 * p];
7223         const PetscInt    *perm  = perms ? perms[p] : NULL;
7224         const PetscScalar *flip  = flips ? flips[p] : NULL;
7225         PetscCall(PetscSectionGetDof(section, point, &dof));
7226         PetscCall(updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array));
7227       }
7228       break;
7229     case ADD_VALUES:
7230       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7231         const PetscInt     point = points[2 * p];
7232         const PetscInt    *perm  = perms ? perms[p] : NULL;
7233         const PetscScalar *flip  = flips ? flips[p] : NULL;
7234         PetscCall(PetscSectionGetDof(section, point, &dof));
7235         PetscCall(updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array));
7236       }
7237       break;
7238     case ADD_ALL_VALUES:
7239       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7240         const PetscInt     point = points[2 * p];
7241         const PetscInt    *perm  = perms ? perms[p] : NULL;
7242         const PetscScalar *flip  = flips ? flips[p] : NULL;
7243         PetscCall(PetscSectionGetDof(section, point, &dof));
7244         PetscCall(updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array));
7245       }
7246       break;
7247     case ADD_BC_VALUES:
7248       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7249         const PetscInt     point = points[2 * p];
7250         const PetscInt    *perm  = perms ? perms[p] : NULL;
7251         const PetscScalar *flip  = flips ? flips[p] : NULL;
7252         PetscCall(PetscSectionGetDof(section, point, &dof));
7253         PetscCall(updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array));
7254       }
7255       break;
7256     default:
7257       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7258     }
7259     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
7260   }
7261   /* Cleanup points */
7262   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7263   /* Cleanup array */
7264   PetscCall(VecRestoreArray(v, &array));
7265   PetscFunctionReturn(PETSC_SUCCESS);
7266 }
7267 
7268 /* Check whether the given point is in the label. If not, update the offset to skip this point */
7269 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
7270 {
7271   PetscFunctionBegin;
7272   *contains = PETSC_TRUE;
7273   if (label) {
7274     PetscInt fdof;
7275 
7276     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
7277     if (!*contains) {
7278       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7279       *offset += fdof;
7280       PetscFunctionReturn(PETSC_SUCCESS);
7281     }
7282   }
7283   PetscFunctionReturn(PETSC_SUCCESS);
7284 }
7285 
7286 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
7287 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)
7288 {
7289   PetscSection    clSection;
7290   IS              clPoints;
7291   PetscScalar    *array;
7292   PetscInt       *points = NULL;
7293   const PetscInt *clp;
7294   PetscInt        numFields, numPoints, p;
7295   PetscInt        offset = 0, f;
7296 
7297   PetscFunctionBeginHot;
7298   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7299   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7300   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7301   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7302   PetscCall(PetscSectionGetNumFields(section, &numFields));
7303   /* Get points */
7304   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7305   /* Get array */
7306   PetscCall(VecGetArray(v, &array));
7307   /* Get values */
7308   for (f = 0; f < numFields; ++f) {
7309     const PetscInt    **perms = NULL;
7310     const PetscScalar **flips = NULL;
7311     PetscBool           contains;
7312 
7313     if (!fieldActive[f]) {
7314       for (p = 0; p < numPoints * 2; p += 2) {
7315         PetscInt fdof;
7316         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7317         offset += fdof;
7318       }
7319       continue;
7320     }
7321     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7322     switch (mode) {
7323     case INSERT_VALUES:
7324       for (p = 0; p < numPoints; p++) {
7325         const PetscInt     point = points[2 * p];
7326         const PetscInt    *perm  = perms ? perms[p] : NULL;
7327         const PetscScalar *flip  = flips ? flips[p] : NULL;
7328         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7329         if (!contains) continue;
7330         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
7331       }
7332       break;
7333     case INSERT_ALL_VALUES:
7334       for (p = 0; p < numPoints; p++) {
7335         const PetscInt     point = points[2 * p];
7336         const PetscInt    *perm  = perms ? perms[p] : NULL;
7337         const PetscScalar *flip  = flips ? flips[p] : NULL;
7338         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7339         if (!contains) continue;
7340         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
7341       }
7342       break;
7343     case INSERT_BC_VALUES:
7344       for (p = 0; p < numPoints; p++) {
7345         const PetscInt     point = points[2 * p];
7346         const PetscInt    *perm  = perms ? perms[p] : NULL;
7347         const PetscScalar *flip  = flips ? flips[p] : NULL;
7348         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7349         if (!contains) continue;
7350         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
7351       }
7352       break;
7353     case ADD_VALUES:
7354       for (p = 0; p < numPoints; p++) {
7355         const PetscInt     point = points[2 * p];
7356         const PetscInt    *perm  = perms ? perms[p] : NULL;
7357         const PetscScalar *flip  = flips ? flips[p] : NULL;
7358         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7359         if (!contains) continue;
7360         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
7361       }
7362       break;
7363     case ADD_ALL_VALUES:
7364       for (p = 0; p < numPoints; p++) {
7365         const PetscInt     point = points[2 * p];
7366         const PetscInt    *perm  = perms ? perms[p] : NULL;
7367         const PetscScalar *flip  = flips ? flips[p] : NULL;
7368         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7369         if (!contains) continue;
7370         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
7371       }
7372       break;
7373     default:
7374       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7375     }
7376     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7377   }
7378   /* Cleanup points */
7379   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7380   /* Cleanup array */
7381   PetscCall(VecRestoreArray(v, &array));
7382   PetscFunctionReturn(PETSC_SUCCESS);
7383 }
7384 
7385 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
7386 {
7387   PetscMPIInt rank;
7388   PetscInt    i, j;
7389 
7390   PetscFunctionBegin;
7391   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7392   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
7393   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
7394   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
7395   numCIndices = numCIndices ? numCIndices : numRIndices;
7396   if (!values) PetscFunctionReturn(PETSC_SUCCESS);
7397   for (i = 0; i < numRIndices; i++) {
7398     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
7399     for (j = 0; j < numCIndices; j++) {
7400 #if defined(PETSC_USE_COMPLEX)
7401       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
7402 #else
7403       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
7404 #endif
7405     }
7406     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
7407   }
7408   PetscFunctionReturn(PETSC_SUCCESS);
7409 }
7410 
7411 /*
7412   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
7413 
7414   Input Parameters:
7415 + section - The section for this data layout
7416 . islocal - Is the section (and thus indices being requested) local or global?
7417 . point   - The point contributing dofs with these indices
7418 . off     - The global offset of this point
7419 . loff    - The local offset of each field
7420 . setBC   - The flag determining whether to include indices of boundary values
7421 . perm    - A permutation of the dofs on this point, or NULL
7422 - indperm - A permutation of the entire indices array, or NULL
7423 
7424   Output Parameter:
7425 . indices - Indices for dofs on this point
7426 
7427   Level: developer
7428 
7429   Note: The indices could be local or global, depending on the value of 'off'.
7430 */
7431 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
7432 {
7433   PetscInt        dof;   /* The number of unknowns on this point */
7434   PetscInt        cdof;  /* The number of constraints on this point */
7435   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7436   PetscInt        cind = 0, k;
7437 
7438   PetscFunctionBegin;
7439   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7440   PetscCall(PetscSectionGetDof(section, point, &dof));
7441   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
7442   if (!cdof || setBC) {
7443     for (k = 0; k < dof; ++k) {
7444       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7445       const PetscInt ind    = indperm ? indperm[preind] : preind;
7446 
7447       indices[ind] = off + k;
7448     }
7449   } else {
7450     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
7451     for (k = 0; k < dof; ++k) {
7452       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7453       const PetscInt ind    = indperm ? indperm[preind] : preind;
7454 
7455       if ((cind < cdof) && (k == cdofs[cind])) {
7456         /* Insert check for returning constrained indices */
7457         indices[ind] = -(off + k + 1);
7458         ++cind;
7459       } else {
7460         indices[ind] = off + k - (islocal ? 0 : cind);
7461       }
7462     }
7463   }
7464   *loff += dof;
7465   PetscFunctionReturn(PETSC_SUCCESS);
7466 }
7467 
7468 /*
7469  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
7470 
7471  Input Parameters:
7472 + section - a section (global or local)
7473 - islocal - `PETSC_TRUE` if requesting local indices (i.e., section is local); `PETSC_FALSE` for global
7474 . point - point within section
7475 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
7476 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
7477 . setBC - identify constrained (boundary condition) points via involution.
7478 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
7479 . permsoff - offset
7480 - indperm - index permutation
7481 
7482  Output Parameter:
7483 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
7484 . indices - array to hold indices (as defined by section) of each dof associated with point
7485 
7486  Notes:
7487  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
7488  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
7489  in the local vector.
7490 
7491  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
7492  significant).  It is invalid to call with a global section and setBC=true.
7493 
7494  Developer Note:
7495  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
7496  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
7497  offset could be obtained from the section instead of passing it explicitly as we do now.
7498 
7499  Example:
7500  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
7501  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
7502  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
7503  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.
7504 
7505  Level: developer
7506 */
7507 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[])
7508 {
7509   PetscInt numFields, foff, f;
7510 
7511   PetscFunctionBegin;
7512   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7513   PetscCall(PetscSectionGetNumFields(section, &numFields));
7514   for (f = 0, foff = 0; f < numFields; ++f) {
7515     PetscInt        fdof, cfdof;
7516     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7517     PetscInt        cind = 0, b;
7518     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7519 
7520     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7521     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7522     if (!cfdof || setBC) {
7523       for (b = 0; b < fdof; ++b) {
7524         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7525         const PetscInt ind    = indperm ? indperm[preind] : preind;
7526 
7527         indices[ind] = off + foff + b;
7528       }
7529     } else {
7530       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7531       for (b = 0; b < fdof; ++b) {
7532         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7533         const PetscInt ind    = indperm ? indperm[preind] : preind;
7534 
7535         if ((cind < cfdof) && (b == fcdofs[cind])) {
7536           indices[ind] = -(off + foff + b + 1);
7537           ++cind;
7538         } else {
7539           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7540         }
7541       }
7542     }
7543     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7544     foffs[f] += fdof;
7545   }
7546   PetscFunctionReturn(PETSC_SUCCESS);
7547 }
7548 
7549 /*
7550   This version believes the globalSection offsets for each field, rather than just the point offset
7551 
7552  . foffs - The offset into 'indices' for each field, since it is segregated by field
7553 
7554  Notes:
7555  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7556  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7557 */
7558 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7559 {
7560   PetscInt numFields, foff, f;
7561 
7562   PetscFunctionBegin;
7563   PetscCall(PetscSectionGetNumFields(section, &numFields));
7564   for (f = 0; f < numFields; ++f) {
7565     PetscInt        fdof, cfdof;
7566     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7567     PetscInt        cind = 0, b;
7568     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7569 
7570     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7571     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7572     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7573     if (!cfdof) {
7574       for (b = 0; b < fdof; ++b) {
7575         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7576         const PetscInt ind    = indperm ? indperm[preind] : preind;
7577 
7578         indices[ind] = foff + b;
7579       }
7580     } else {
7581       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7582       for (b = 0; b < fdof; ++b) {
7583         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7584         const PetscInt ind    = indperm ? indperm[preind] : preind;
7585 
7586         if ((cind < cfdof) && (b == fcdofs[cind])) {
7587           indices[ind] = -(foff + b + 1);
7588           ++cind;
7589         } else {
7590           indices[ind] = foff + b - cind;
7591         }
7592       }
7593     }
7594     foffs[f] += fdof;
7595   }
7596   PetscFunctionReturn(PETSC_SUCCESS);
7597 }
7598 
7599 static PetscErrorCode DMPlexAnchorsGetSubMatIndices(PetscInt nPoints, const PetscInt pnts[], PetscSection section, PetscSection cSec, PetscInt tmpIndices[], PetscInt fieldOffsets[], PetscInt indices[], const PetscInt ***perms)
7600 {
7601   PetscInt numFields, sStart, sEnd, cStart, cEnd;
7602 
7603   PetscFunctionBegin;
7604   PetscCall(PetscSectionGetNumFields(section, &numFields));
7605   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7606   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7607   for (PetscInt p = 0; p < nPoints; p++) {
7608     PetscInt     b       = pnts[2 * p];
7609     PetscInt     bSecDof = 0, bOff;
7610     PetscInt     cSecDof = 0;
7611     PetscSection indices_section;
7612 
7613     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7614     if (!bSecDof) continue;
7615     if (b >= cStart && b < cEnd) PetscCall(PetscSectionGetDof(cSec, b, &cSecDof));
7616     indices_section = cSecDof > 0 ? cSec : section;
7617     if (numFields) {
7618       PetscInt fStart[32], fEnd[32];
7619 
7620       fStart[0] = 0;
7621       fEnd[0]   = 0;
7622       for (PetscInt f = 0; f < numFields; f++) {
7623         PetscInt fDof = 0;
7624 
7625         PetscCall(PetscSectionGetFieldDof(indices_section, b, f, &fDof));
7626         fStart[f + 1] = fStart[f] + fDof;
7627         fEnd[f + 1]   = fStart[f + 1];
7628       }
7629       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7630       // only apply permutations on one side
7631       PetscCall(DMPlexGetIndicesPointFields_Internal(indices_section, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, perms ? p : -1, NULL, tmpIndices));
7632       for (PetscInt f = 0; f < numFields; f++) {
7633         for (PetscInt i = fStart[f]; i < fEnd[f]; i++) { indices[fieldOffsets[f]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1); }
7634       }
7635     } else {
7636       PetscInt bEnd = 0;
7637 
7638       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7639       PetscCall(DMPlexGetIndicesPoint_Internal(indices_section, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, tmpIndices));
7640 
7641       for (PetscInt i = 0; i < bEnd; i++) indices[fieldOffsets[0]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1);
7642     }
7643   }
7644   PetscFunctionReturn(PETSC_SUCCESS);
7645 }
7646 
7647 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[])
7648 {
7649   Mat             cMat;
7650   PetscSection    aSec, cSec;
7651   IS              aIS;
7652   PetscInt        aStart = -1, aEnd = -1;
7653   PetscInt        sStart = -1, sEnd = -1;
7654   PetscInt        cStart = -1, cEnd = -1;
7655   const PetscInt *anchors;
7656   PetscInt        numFields, p;
7657   PetscInt        newNumPoints = 0, newNumIndices = 0;
7658   PetscInt       *newPoints, *indices, *newIndices, *tmpIndices, *tmpNewIndices;
7659   PetscInt        oldOffsets[32];
7660   PetscInt        newOffsets[32];
7661   PetscInt        oldOffsetsCopy[32];
7662   PetscInt        newOffsetsCopy[32];
7663   PetscScalar    *modMat         = NULL;
7664   PetscBool       anyConstrained = PETSC_FALSE;
7665 
7666   PetscFunctionBegin;
7667   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7668   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7669   PetscCall(PetscSectionGetNumFields(section, &numFields));
7670 
7671   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7672   /* if there are point-to-point constraints */
7673   if (aSec) {
7674     PetscCall(PetscArrayzero(newOffsets, 32));
7675     PetscCall(PetscArrayzero(oldOffsets, 32));
7676     PetscCall(ISGetIndices(aIS, &anchors));
7677     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7678     PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7679     /* figure out how many points are going to be in the new element matrix
7680      * (we allow double counting, because it's all just going to be summed
7681      * into the global matrix anyway) */
7682     for (p = 0; p < 2 * numPoints; p += 2) {
7683       PetscInt b    = points[p];
7684       PetscInt bDof = 0, bSecDof = 0;
7685 
7686       if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7687       if (!bSecDof) continue;
7688 
7689       for (PetscInt f = 0; f < numFields; f++) {
7690         PetscInt fDof = 0;
7691 
7692         PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7693         oldOffsets[f + 1] += fDof;
7694       }
7695       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7696       if (bDof) {
7697         /* this point is constrained */
7698         /* it is going to be replaced by its anchors */
7699         PetscInt bOff, q;
7700 
7701         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7702         for (q = 0; q < bDof; q++) {
7703           PetscInt a    = anchors[bOff + q];
7704           PetscInt aDof = 0;
7705 
7706           if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7707           if (aDof) {
7708             anyConstrained = PETSC_TRUE;
7709             newNumPoints += 1;
7710           }
7711           newNumIndices += aDof;
7712           for (PetscInt f = 0; f < numFields; ++f) {
7713             PetscInt fDof = 0;
7714 
7715             if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7716             newOffsets[f + 1] += fDof;
7717           }
7718         }
7719       } else {
7720         /* this point is not constrained */
7721         newNumPoints++;
7722         newNumIndices += bSecDof;
7723         for (PetscInt f = 0; f < numFields; ++f) {
7724           PetscInt fDof;
7725 
7726           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7727           newOffsets[f + 1] += fDof;
7728         }
7729       }
7730     }
7731   }
7732   if (!anyConstrained) {
7733     if (outNumPoints) *outNumPoints = 0;
7734     if (outNumIndices) *outNumIndices = 0;
7735     if (outPoints) *outPoints = NULL;
7736     if (outMat) *outMat = NULL;
7737     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7738     PetscFunctionReturn(PETSC_SUCCESS);
7739   }
7740 
7741   if (outNumPoints) *outNumPoints = newNumPoints;
7742   if (outNumIndices) *outNumIndices = newNumIndices;
7743 
7744   for (PetscInt f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7745   for (PetscInt f = 0; f < numFields; ++f) oldOffsets[f + 1] += oldOffsets[f];
7746 
7747   if (!outPoints && !outMat) {
7748     if (offsets) {
7749       for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7750     }
7751     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7752     PetscFunctionReturn(PETSC_SUCCESS);
7753   }
7754 
7755   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7756   PetscCheck(!numFields || oldOffsets[numFields] == numIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, oldOffsets[numFields], numIndices);
7757 
7758   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7759   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7760 
7761   /* output arrays */
7762   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7763   PetscCall(PetscArrayzero(newPoints, 2 * newNumPoints));
7764 
7765   // get the new Points
7766   for (PetscInt p = 0, newP = 0; p < numPoints; p++) {
7767     PetscInt b    = points[2 * p];
7768     PetscInt bDof = 0, bSecDof = 0, bOff;
7769 
7770     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7771     if (!bSecDof) continue;
7772     if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7773     if (bDof) {
7774       PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7775       for (PetscInt q = 0; q < bDof; q++) {
7776         PetscInt a = anchors[bOff + q], aDof = 0;
7777 
7778         if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7779         if (aDof) {
7780           newPoints[2 * newP]     = a;
7781           newPoints[2 * newP + 1] = 0; // orientations are accounted for in constructing the matrix, newly added points are in default orientation
7782           newP++;
7783         }
7784       }
7785     } else {
7786       newPoints[2 * newP]     = b;
7787       newPoints[2 * newP + 1] = points[2 * p + 1];
7788       newP++;
7789     }
7790   }
7791 
7792   if (outMat) {
7793     PetscScalar *tmpMat;
7794     PetscCall(PetscArraycpy(oldOffsetsCopy, oldOffsets, 32));
7795     PetscCall(PetscArraycpy(newOffsetsCopy, newOffsets, 32));
7796 
7797     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &indices));
7798     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7799     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7800     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7801 
7802     for (PetscInt i = 0; i < numIndices; i++) indices[i] = -1;
7803     for (PetscInt i = 0; i < newNumIndices; i++) newIndices[i] = -1;
7804 
7805     PetscCall(DMPlexAnchorsGetSubMatIndices(numPoints, points, section, cSec, tmpIndices, oldOffsetsCopy, indices, perms));
7806     PetscCall(DMPlexAnchorsGetSubMatIndices(newNumPoints, newPoints, section, section, tmpNewIndices, newOffsetsCopy, newIndices, NULL));
7807 
7808     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
7809     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7810     PetscCall(PetscArrayzero(modMat, newNumIndices * numIndices));
7811     // for each field, insert the anchor modification into modMat
7812     for (PetscInt f = 0; f < PetscMax(1, numFields); f++) {
7813       PetscInt fStart    = oldOffsets[f];
7814       PetscInt fNewStart = newOffsets[f];
7815       for (PetscInt p = 0, newP = 0, o = fStart, oNew = fNewStart; p < numPoints; p++) {
7816         PetscInt b    = points[2 * p];
7817         PetscInt bDof = 0, bSecDof = 0, bOff;
7818 
7819         if (b >= sStart && b < sEnd) {
7820           if (numFields) {
7821             PetscCall(PetscSectionGetFieldDof(section, b, f, &bSecDof));
7822           } else {
7823             PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7824           }
7825         }
7826         if (!bSecDof) continue;
7827         if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7828         if (bDof) {
7829           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7830           for (PetscInt q = 0; q < bDof; q++, newP++) {
7831             PetscInt a = anchors[bOff + q], aDof = 0;
7832 
7833             if (a >= sStart && a < sEnd) {
7834               if (numFields) {
7835                 PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
7836               } else {
7837                 PetscCall(PetscSectionGetDof(section, a, &aDof));
7838               }
7839             }
7840             if (aDof) {
7841               PetscCall(MatGetValues(cMat, bSecDof, &indices[o], aDof, &newIndices[oNew], tmpMat));
7842               for (PetscInt d = 0; d < bSecDof; d++) {
7843                 for (PetscInt e = 0; e < aDof; e++) modMat[(o + d) * newNumIndices + oNew + e] = tmpMat[d * aDof + e];
7844               }
7845             }
7846             oNew += aDof;
7847           }
7848         } else {
7849           // Insert the identity matrix in this block
7850           for (PetscInt d = 0; d < bSecDof; d++) modMat[(o + d) * newNumIndices + oNew + d] = 1;
7851           oNew += bSecDof;
7852           newP++;
7853         }
7854         o += bSecDof;
7855       }
7856     }
7857 
7858     *outMat = modMat;
7859 
7860     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7861     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7862     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7863     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7864     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &indices));
7865   }
7866   PetscCall(ISRestoreIndices(aIS, &anchors));
7867 
7868   /* output */
7869   if (outPoints) {
7870     *outPoints = newPoints;
7871   } else {
7872     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7873   }
7874   for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7875   PetscFunctionReturn(PETSC_SUCCESS);
7876 }
7877 
7878 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)
7879 {
7880   PetscScalar *modMat        = NULL;
7881   PetscInt     newNumIndices = -1;
7882 
7883   PetscFunctionBegin;
7884   /* 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.
7885      modMat is that matrix C */
7886   PetscCall(DMPlexAnchorsGetSubMatModification(dm, section, numPoints, numIndices, points, perms, outNumPoints, &newNumIndices, outPoints, offsets, outValues ? &modMat : NULL));
7887   if (outNumIndices) *outNumIndices = newNumIndices;
7888   if (modMat) {
7889     const PetscScalar *newValues = values;
7890 
7891     if (multiplyRight) {
7892       PetscScalar *newNewValues = NULL;
7893       PetscBLASInt M            = newNumIndices;
7894       PetscBLASInt N            = numRows;
7895       PetscBLASInt K            = numIndices;
7896       PetscScalar  a = 1.0, b = 0.0;
7897 
7898       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);
7899 
7900       PetscCall(DMGetWorkArray(dm, numRows * newNumIndices, MPIU_SCALAR, &newNewValues));
7901       // row-major to column-major conversion, right multiplication becomes left multiplication
7902       PetscCallBLAS("BLASgemm", BLASgemm_("N", "N", &M, &N, &K, &a, modMat, &M, newValues, &K, &b, newNewValues, &M));
7903 
7904       numCols   = newNumIndices;
7905       newValues = newNewValues;
7906     }
7907 
7908     if (multiplyLeft) {
7909       PetscScalar *newNewValues = NULL;
7910       PetscBLASInt M            = numCols;
7911       PetscBLASInt N            = newNumIndices;
7912       PetscBLASInt K            = numIndices;
7913       PetscScalar  a = 1.0, b = 0.0;
7914 
7915       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);
7916 
7917       PetscCall(DMGetWorkArray(dm, newNumIndices * numCols, MPIU_SCALAR, &newNewValues));
7918       // row-major to column-major conversion, left multiplication becomes right multiplication
7919       PetscCallBLAS("BLASgemm", BLASgemm_("N", "T", &M, &N, &K, &a, newValues, &M, modMat, &N, &b, newNewValues, &M));
7920       if (newValues != values) PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &newValues));
7921       newValues = newNewValues;
7922     }
7923     *outValues = (PetscScalar *)newValues;
7924     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
7925   }
7926   PetscFunctionReturn(PETSC_SUCCESS);
7927 }
7928 
7929 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)
7930 {
7931   PetscFunctionBegin;
7932   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, numPoints, numIndices, points, perms, numIndices, numIndices, values, outNumPoints, outNumIndices, outPoints, outValues, offsets, PETSC_TRUE, multiplyLeft));
7933   PetscFunctionReturn(PETSC_SUCCESS);
7934 }
7935 
7936 static PetscErrorCode DMPlexGetClosureIndicesSize_Internal(DM dm, PetscSection section, PetscInt point, PetscInt *closureSize)
7937 {
7938   /* Closure ordering */
7939   PetscSection    clSection;
7940   IS              clPoints;
7941   const PetscInt *clp;
7942   PetscInt       *points;
7943   PetscInt        Ncl, Ni = 0;
7944 
7945   PetscFunctionBeginHot;
7946   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7947   for (PetscInt p = 0; p < Ncl * 2; p += 2) {
7948     PetscInt dof;
7949 
7950     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7951     Ni += dof;
7952   }
7953   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7954   *closureSize = Ni;
7955   PetscFunctionReturn(PETSC_SUCCESS);
7956 }
7957 
7958 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)
7959 {
7960   /* Closure ordering */
7961   PetscSection    clSection;
7962   IS              clPoints;
7963   const PetscInt *clp;
7964   PetscInt       *points;
7965   const PetscInt *clperm = NULL;
7966   /* Dof permutation and sign flips */
7967   const PetscInt    **perms[32] = {NULL};
7968   const PetscScalar **flips[32] = {NULL};
7969   PetscScalar        *valCopy   = NULL;
7970   /* Hanging node constraints */
7971   PetscInt    *pointsC = NULL;
7972   PetscScalar *valuesC = NULL;
7973   PetscInt     NclC, NiC;
7974 
7975   PetscInt *idx;
7976   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7977   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7978   PetscInt  idxStart, idxEnd;
7979   PetscInt  nRows, nCols;
7980 
7981   PetscFunctionBeginHot;
7982   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7983   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7984   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7985   PetscAssertPointer(numRows, 6);
7986   PetscAssertPointer(numCols, 7);
7987   if (indices) PetscAssertPointer(indices, 8);
7988   if (outOffsets) PetscAssertPointer(outOffsets, 9);
7989   if (values) PetscAssertPointer(values, 10);
7990   PetscCall(PetscSectionGetNumFields(section, &Nf));
7991   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7992   PetscCall(PetscArrayzero(offsets, 32));
7993   /* 1) Get points in closure */
7994   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7995   if (useClPerm) {
7996     PetscInt depth, clsize;
7997     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7998     for (clsize = 0, p = 0; p < Ncl; p++) {
7999       PetscInt dof;
8000       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
8001       clsize += dof;
8002     }
8003     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
8004   }
8005   /* 2) Get number of indices on these points and field offsets from section */
8006   for (p = 0; p < Ncl * 2; p += 2) {
8007     PetscInt dof, fdof;
8008 
8009     PetscCall(PetscSectionGetDof(section, points[p], &dof));
8010     for (f = 0; f < Nf; ++f) {
8011       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
8012       offsets[f + 1] += fdof;
8013     }
8014     Ni += dof;
8015   }
8016   if (*numRows == -1) *numRows = Ni;
8017   if (*numCols == -1) *numCols = Ni;
8018   nRows = *numRows;
8019   nCols = *numCols;
8020   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
8021   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
8022   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
8023   if (multiplyRight) PetscCheck(nCols == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " columns, got %" PetscInt_FMT, Ni, nCols);
8024   if (multiplyLeft) PetscCheck(nRows == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " rows, got %" PetscInt_FMT, Ni, nRows);
8025   for (f = 0; f < PetscMax(1, Nf); ++f) {
8026     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8027     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
8028     /* may need to apply sign changes to the element matrix */
8029     if (values && flips[f]) {
8030       PetscInt foffset = offsets[f];
8031 
8032       for (p = 0; p < Ncl; ++p) {
8033         PetscInt           pnt  = points[2 * p], fdof;
8034         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
8035 
8036         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
8037         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
8038         if (flip) {
8039           PetscInt i, j, k;
8040 
8041           if (!valCopy) {
8042             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8043             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
8044             *values = valCopy;
8045           }
8046           for (i = 0; i < fdof; ++i) {
8047             PetscScalar fval = flip[i];
8048 
8049             if (multiplyRight) {
8050               for (k = 0; k < nRows; ++k) { valCopy[Ni * k + (foffset + i)] *= fval; }
8051             }
8052             if (multiplyLeft) {
8053               for (k = 0; k < nCols; ++k) { valCopy[nCols * (foffset + i) + k] *= fval; }
8054             }
8055           }
8056         }
8057         foffset += fdof;
8058       }
8059     }
8060   }
8061   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
8062   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, Ncl, Ni, points, perms, nRows, nCols, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, multiplyRight, multiplyLeft));
8063   if (NclC) {
8064     if (multiplyRight) { *numCols = nCols = NiC; }
8065     if (multiplyLeft) { *numRows = nRows = NiC; }
8066     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8067     for (f = 0; f < PetscMax(1, Nf); ++f) {
8068       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8069       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8070     }
8071     for (f = 0; f < PetscMax(1, Nf); ++f) {
8072       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
8073       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
8074     }
8075     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8076     Ncl    = NclC;
8077     Ni     = NiC;
8078     points = pointsC;
8079     if (values) *values = valuesC;
8080   }
8081   /* 5) Calculate indices */
8082   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
8083   PetscCall(PetscSectionGetChart(idxSection, &idxStart, &idxEnd));
8084   if (Nf) {
8085     PetscInt  idxOff;
8086     PetscBool useFieldOffsets;
8087 
8088     if (outOffsets) {
8089       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
8090     }
8091     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
8092     if (useFieldOffsets) {
8093       for (p = 0; p < Ncl; ++p) {
8094         const PetscInt pnt = points[p * 2];
8095 
8096         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
8097       }
8098     } else {
8099       for (p = 0; p < Ncl; ++p) {
8100         const PetscInt pnt = points[p * 2];
8101 
8102         if (pnt < idxStart || pnt >= idxEnd) continue;
8103         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8104         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8105          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
8106          * global section. */
8107         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
8108       }
8109     }
8110   } else {
8111     PetscInt off = 0, idxOff;
8112 
8113     for (p = 0; p < Ncl; ++p) {
8114       const PetscInt  pnt  = points[p * 2];
8115       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
8116 
8117       if (pnt < idxStart || pnt >= idxEnd) continue;
8118       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8119       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8120        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
8121       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
8122     }
8123   }
8124   /* 6) Cleanup */
8125   for (f = 0; f < PetscMax(1, Nf); ++f) {
8126     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8127     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8128   }
8129   if (NclC) {
8130     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
8131   } else {
8132     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8133   }
8134 
8135   if (indices) *indices = idx;
8136   PetscFunctionReturn(PETSC_SUCCESS);
8137 }
8138 
8139 /*@C
8140   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
8141 
8142   Not collective
8143 
8144   Input Parameters:
8145 + dm         - The `DM`
8146 . section    - The `PetscSection` describing the points (a local section)
8147 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8148 . point      - The point defining the closure
8149 - useClPerm  - Use the closure point permutation if available
8150 
8151   Output Parameters:
8152 + numIndices - The number of dof indices in the closure of point with the input sections
8153 . indices    - The dof indices
8154 . outOffsets - Array to write the field offsets into, or `NULL`
8155 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8156 
8157   Level: advanced
8158 
8159   Notes:
8160   Call `DMPlexRestoreClosureIndices()` to free allocated memory
8161 
8162   If `idxSection` is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8163   of those indices is not significant.  If `idxSection` is local, the constrained dofs will yield the involution -(idx+1)
8164   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8165   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when `idxSection` == section, otherwise global
8166   indices (with the above semantics) are implied.
8167 
8168 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
8169           `PetscSection`, `DMGetGlobalSection()`
8170 @*/
8171 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
8172 {
8173   PetscInt numRows = -1, numCols = -1;
8174 
8175   PetscFunctionBeginHot;
8176   PetscCall(DMPlexGetClosureIndices_Internal(dm, section, idxSection, point, useClPerm, &numRows, &numCols, indices, outOffsets, values, PETSC_TRUE, PETSC_TRUE));
8177   PetscCheck(numRows == numCols, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Symmetric matrix transformation produces rectangular dimensions (%" PetscInt_FMT ", %" PetscInt_FMT ")", numRows, numCols);
8178   *numIndices = numRows;
8179   PetscFunctionReturn(PETSC_SUCCESS);
8180 }
8181 
8182 /*@C
8183   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
8184 
8185   Not collective
8186 
8187   Input Parameters:
8188 + dm         - The `DM`
8189 . section    - The `PetscSection` describing the points (a local section)
8190 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8191 . point      - The point defining the closure
8192 - useClPerm  - Use the closure point permutation if available
8193 
8194   Output Parameters:
8195 + numIndices - The number of dof indices in the closure of point with the input sections
8196 . indices    - The dof indices
8197 . outOffsets - Array to write the field offsets into, or `NULL`
8198 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8199 
8200   Level: advanced
8201 
8202   Notes:
8203   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
8204 
8205   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8206   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
8207   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8208   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
8209   indices (with the above semantics) are implied.
8210 
8211 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
8212 @*/
8213 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
8214 {
8215   PetscFunctionBegin;
8216   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8217   PetscAssertPointer(indices, 7);
8218   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
8219   PetscFunctionReturn(PETSC_SUCCESS);
8220 }
8221 
8222 PetscErrorCode DMPlexMatSetClosure_Internal(DM dm, PetscSection section, PetscSection globalSection, PetscBool useClPerm, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8223 {
8224   DM_Plex           *mesh = (DM_Plex *)dm->data;
8225   PetscInt          *indices;
8226   PetscInt           numIndices;
8227   const PetscScalar *valuesOrig = values;
8228   PetscErrorCode     ierr;
8229 
8230   PetscFunctionBegin;
8231   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8232   if (!section) PetscCall(DMGetLocalSection(dm, &section));
8233   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
8234   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
8235   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
8236   PetscValidHeaderSpecific(A, MAT_CLASSID, 5);
8237 
8238   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, useClPerm, &numIndices, &indices, NULL, (PetscScalar **)&values));
8239 
8240   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
8241   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8242   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
8243   if (ierr) {
8244     PetscMPIInt rank;
8245 
8246     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8247     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8248     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
8249     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8250     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8251     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
8252   }
8253   if (mesh->printFEM > 1) {
8254     PetscInt i;
8255     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
8256     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
8257     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8258   }
8259 
8260   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8261   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8262   PetscFunctionReturn(PETSC_SUCCESS);
8263 }
8264 
8265 /*@C
8266   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
8267 
8268   Not collective
8269 
8270   Input Parameters:
8271 + dm            - The `DM`
8272 . section       - The section describing the layout in `v`, or `NULL` to use the default section
8273 . globalSection - The section describing the layout in `v`, or `NULL` to use the default global section
8274 . A             - The matrix
8275 . point         - The point in the `DM`
8276 . values        - The array of values
8277 - mode          - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8278 
8279   Level: intermediate
8280 
8281 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8282 @*/
8283 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8284 {
8285   PetscFunctionBegin;
8286   PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, PETSC_TRUE, A, point, values, mode));
8287   PetscFunctionReturn(PETSC_SUCCESS);
8288 }
8289 
8290 /*@C
8291   DMPlexMatSetClosureGeneral - Set an array of the values on the closure of 'point' using a different row and column section
8292 
8293   Not collective
8294 
8295   Input Parameters:
8296 + dmRow            - The `DM` for the row fields
8297 . sectionRow       - The section describing the layout, or `NULL` to use the default section in `dmRow`
8298 . useRowPerm       - The flag to use the closure permutation of the `dmRow` if available
8299 . globalSectionRow - The section describing the layout, or `NULL` to use the default global section in `dmRow`
8300 . dmCol            - The `DM` for the column fields
8301 . sectionCol       - The section describing the layout, or `NULL` to use the default section in `dmCol`
8302 . useColPerm       - The flag to use the closure permutation of the `dmCol` if available
8303 . globalSectionCol - The section describing the layout, or `NULL` to use the default global section in `dmCol`
8304 . A                - The matrix
8305 . point            - The point in the `DM`
8306 . values           - The array of values
8307 - mode             - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8308 
8309   Level: intermediate
8310 
8311 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8312 @*/
8313 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)
8314 {
8315   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
8316   PetscInt          *indicesRow, *indicesCol;
8317   PetscInt           numIndicesRow = -1, numIndicesCol = -1;
8318   const PetscScalar *valuesV0 = values, *valuesV1, *valuesV2;
8319 
8320   PetscErrorCode ierr;
8321 
8322   PetscFunctionBegin;
8323   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
8324   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
8325   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
8326   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
8327   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
8328   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 5);
8329   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
8330   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 6);
8331   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
8332   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 7);
8333   PetscValidHeaderSpecific(A, MAT_CLASSID, 9);
8334 
8335   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmRow, sectionRow, point, &numIndicesRow));
8336   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmCol, sectionCol, point, &numIndicesCol));
8337   valuesV1 = valuesV0;
8338   PetscCall(DMPlexGetClosureIndices_Internal(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV1, PETSC_FALSE, PETSC_TRUE));
8339   valuesV2 = valuesV1;
8340   PetscCall(DMPlexGetClosureIndices_Internal(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesRow, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2, PETSC_TRUE, PETSC_FALSE));
8341 
8342   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2));
8343   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8344   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2, mode);
8345   if (ierr) {
8346     PetscMPIInt rank;
8347 
8348     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8349     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8350     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
8351     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV2));
8352     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8353     if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8354     if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8355   }
8356 
8357   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2));
8358   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8359   if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8360   if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8361   PetscFunctionReturn(PETSC_SUCCESS);
8362 }
8363 
8364 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8365 {
8366   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
8367   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8368   PetscInt       *cpoints = NULL;
8369   PetscInt       *findices, *cindices;
8370   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8371   PetscInt        foffsets[32], coffsets[32];
8372   DMPolytopeType  ct;
8373   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8374   PetscErrorCode  ierr;
8375 
8376   PetscFunctionBegin;
8377   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8378   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8379   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8380   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8381   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8382   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8383   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8384   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8385   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8386   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8387   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
8388   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8389   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8390   PetscCall(PetscArrayzero(foffsets, 32));
8391   PetscCall(PetscArrayzero(coffsets, 32));
8392   /* Column indices */
8393   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8394   maxFPoints = numCPoints;
8395   /* Compress out points not in the section */
8396   /*   TODO: Squeeze out points with 0 dof as well */
8397   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8398   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8399     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8400       cpoints[q * 2]     = cpoints[p];
8401       cpoints[q * 2 + 1] = cpoints[p + 1];
8402       ++q;
8403     }
8404   }
8405   numCPoints = q;
8406   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8407     PetscInt fdof;
8408 
8409     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8410     if (!dof) continue;
8411     for (f = 0; f < numFields; ++f) {
8412       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8413       coffsets[f + 1] += fdof;
8414     }
8415     numCIndices += dof;
8416   }
8417   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8418   /* Row indices */
8419   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8420   {
8421     DMPlexTransform tr;
8422     DMPolytopeType *rct;
8423     PetscInt       *rsize, *rcone, *rornt, Nt;
8424 
8425     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8426     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8427     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8428     numSubcells = rsize[Nt - 1];
8429     PetscCall(DMPlexTransformDestroy(&tr));
8430   }
8431   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8432   for (r = 0, q = 0; r < numSubcells; ++r) {
8433     /* TODO Map from coarse to fine cells */
8434     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8435     /* Compress out points not in the section */
8436     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8437     for (p = 0; p < numFPoints * 2; p += 2) {
8438       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8439         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8440         if (!dof) continue;
8441         for (s = 0; s < q; ++s)
8442           if (fpoints[p] == ftotpoints[s * 2]) break;
8443         if (s < q) continue;
8444         ftotpoints[q * 2]     = fpoints[p];
8445         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8446         ++q;
8447       }
8448     }
8449     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8450   }
8451   numFPoints = q;
8452   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8453     PetscInt fdof;
8454 
8455     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8456     if (!dof) continue;
8457     for (f = 0; f < numFields; ++f) {
8458       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8459       foffsets[f + 1] += fdof;
8460     }
8461     numFIndices += dof;
8462   }
8463   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8464 
8465   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8466   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8467   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8468   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8469   if (numFields) {
8470     const PetscInt **permsF[32] = {NULL};
8471     const PetscInt **permsC[32] = {NULL};
8472 
8473     for (f = 0; f < numFields; f++) {
8474       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8475       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8476     }
8477     for (p = 0; p < numFPoints; p++) {
8478       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8479       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8480     }
8481     for (p = 0; p < numCPoints; p++) {
8482       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8483       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8484     }
8485     for (f = 0; f < numFields; f++) {
8486       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8487       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8488     }
8489   } else {
8490     const PetscInt **permsF = NULL;
8491     const PetscInt **permsC = NULL;
8492 
8493     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8494     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8495     for (p = 0, off = 0; p < numFPoints; p++) {
8496       const PetscInt *perm = permsF ? permsF[p] : NULL;
8497 
8498       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8499       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8500     }
8501     for (p = 0, off = 0; p < numCPoints; p++) {
8502       const PetscInt *perm = permsC ? permsC[p] : NULL;
8503 
8504       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8505       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8506     }
8507     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8508     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8509   }
8510   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8511   /* TODO: flips */
8512   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8513   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8514   if (ierr) {
8515     PetscMPIInt rank;
8516 
8517     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8518     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8519     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8520     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8521     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8522   }
8523   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8524   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8525   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8526   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8527   PetscFunctionReturn(PETSC_SUCCESS);
8528 }
8529 
8530 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8531 {
8532   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8533   PetscInt       *cpoints      = NULL;
8534   PetscInt        foffsets[32] = {0}, coffsets[32] = {0};
8535   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8536   DMPolytopeType  ct;
8537   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8538 
8539   PetscFunctionBegin;
8540   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8541   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8542   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8543   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8544   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8545   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8546   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8547   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8548   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8549   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8550   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8551   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8552   /* Column indices */
8553   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8554   maxFPoints = numCPoints;
8555   /* Compress out points not in the section */
8556   /*   TODO: Squeeze out points with 0 dof as well */
8557   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8558   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8559     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8560       cpoints[q * 2]     = cpoints[p];
8561       cpoints[q * 2 + 1] = cpoints[p + 1];
8562       ++q;
8563     }
8564   }
8565   numCPoints = q;
8566   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8567     PetscInt fdof;
8568 
8569     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8570     if (!dof) continue;
8571     for (f = 0; f < numFields; ++f) {
8572       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8573       coffsets[f + 1] += fdof;
8574     }
8575     numCIndices += dof;
8576   }
8577   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8578   /* Row indices */
8579   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8580   {
8581     DMPlexTransform tr;
8582     DMPolytopeType *rct;
8583     PetscInt       *rsize, *rcone, *rornt, Nt;
8584 
8585     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8586     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8587     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8588     numSubcells = rsize[Nt - 1];
8589     PetscCall(DMPlexTransformDestroy(&tr));
8590   }
8591   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8592   for (r = 0, q = 0; r < numSubcells; ++r) {
8593     /* TODO Map from coarse to fine cells */
8594     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8595     /* Compress out points not in the section */
8596     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8597     for (p = 0; p < numFPoints * 2; p += 2) {
8598       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8599         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8600         if (!dof) continue;
8601         for (s = 0; s < q; ++s)
8602           if (fpoints[p] == ftotpoints[s * 2]) break;
8603         if (s < q) continue;
8604         ftotpoints[q * 2]     = fpoints[p];
8605         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8606         ++q;
8607       }
8608     }
8609     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8610   }
8611   numFPoints = q;
8612   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8613     PetscInt fdof;
8614 
8615     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8616     if (!dof) continue;
8617     for (f = 0; f < numFields; ++f) {
8618       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8619       foffsets[f + 1] += fdof;
8620     }
8621     numFIndices += dof;
8622   }
8623   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8624 
8625   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8626   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8627   if (numFields) {
8628     const PetscInt **permsF[32] = {NULL};
8629     const PetscInt **permsC[32] = {NULL};
8630 
8631     for (f = 0; f < numFields; f++) {
8632       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8633       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8634     }
8635     for (p = 0; p < numFPoints; p++) {
8636       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8637       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8638     }
8639     for (p = 0; p < numCPoints; p++) {
8640       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8641       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8642     }
8643     for (f = 0; f < numFields; f++) {
8644       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8645       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8646     }
8647   } else {
8648     const PetscInt **permsF = NULL;
8649     const PetscInt **permsC = NULL;
8650 
8651     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8652     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8653     for (p = 0, off = 0; p < numFPoints; p++) {
8654       const PetscInt *perm = permsF ? permsF[p] : NULL;
8655 
8656       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8657       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8658     }
8659     for (p = 0, off = 0; p < numCPoints; p++) {
8660       const PetscInt *perm = permsC ? permsC[p] : NULL;
8661 
8662       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8663       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8664     }
8665     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8666     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8667   }
8668   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8669   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8670   PetscFunctionReturn(PETSC_SUCCESS);
8671 }
8672 
8673 /*@
8674   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8675 
8676   Input Parameter:
8677 . dm - The `DMPLEX` object
8678 
8679   Output Parameter:
8680 . cellHeight - The height of a cell
8681 
8682   Level: developer
8683 
8684 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetVTKCellHeight()`
8685 @*/
8686 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8687 {
8688   DM_Plex *mesh = (DM_Plex *)dm->data;
8689 
8690   PetscFunctionBegin;
8691   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8692   PetscAssertPointer(cellHeight, 2);
8693   *cellHeight = mesh->vtkCellHeight;
8694   PetscFunctionReturn(PETSC_SUCCESS);
8695 }
8696 
8697 /*@
8698   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8699 
8700   Input Parameters:
8701 + dm         - The `DMPLEX` object
8702 - cellHeight - The height of a cell
8703 
8704   Level: developer
8705 
8706 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8707 @*/
8708 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8709 {
8710   DM_Plex *mesh = (DM_Plex *)dm->data;
8711 
8712   PetscFunctionBegin;
8713   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8714   mesh->vtkCellHeight = cellHeight;
8715   PetscFunctionReturn(PETSC_SUCCESS);
8716 }
8717 
8718 /*@
8719   DMPlexGetCellTypeStratum - Get the range of cells of a given celltype
8720 
8721   Input Parameters:
8722 + dm - The `DMPLEX` object
8723 - ct - The `DMPolytopeType` of the cell
8724 
8725   Output Parameters:
8726 + start - The first cell of this type, or `NULL`
8727 - end   - The upper bound on this celltype, or `NULL`
8728 
8729   Level: advanced
8730 
8731 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8732 @*/
8733 PetscErrorCode DMPlexGetCellTypeStratum(DM dm, DMPolytopeType ct, PetscInt *start, PetscInt *end)
8734 {
8735   DM_Plex *mesh = (DM_Plex *)dm->data;
8736   DMLabel  label;
8737   PetscInt pStart, pEnd;
8738 
8739   PetscFunctionBegin;
8740   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8741   if (start) {
8742     PetscAssertPointer(start, 3);
8743     *start = 0;
8744   }
8745   if (end) {
8746     PetscAssertPointer(end, 4);
8747     *end = 0;
8748   }
8749   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8750   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
8751   if (mesh->tr) {
8752     PetscCall(DMPlexTransformGetCellTypeStratum(mesh->tr, ct, start, end));
8753   } else {
8754     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
8755     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named celltype was found");
8756     PetscCall(DMLabelGetStratumBounds(label, ct, start, end));
8757   }
8758   PetscFunctionReturn(PETSC_SUCCESS);
8759 }
8760 
8761 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8762 {
8763   PetscSection section, globalSection;
8764   PetscInt    *numbers, p;
8765 
8766   PetscFunctionBegin;
8767   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8768   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8769   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8770   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8771   PetscCall(PetscSectionSetUp(section));
8772   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &globalSection));
8773   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8774   for (p = pStart; p < pEnd; ++p) {
8775     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8776     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8777     else numbers[p - pStart] += shift;
8778   }
8779   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8780   if (globalSize) {
8781     PetscLayout layout;
8782     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8783     PetscCall(PetscLayoutGetSize(layout, globalSize));
8784     PetscCall(PetscLayoutDestroy(&layout));
8785   }
8786   PetscCall(PetscSectionDestroy(&section));
8787   PetscCall(PetscSectionDestroy(&globalSection));
8788   PetscFunctionReturn(PETSC_SUCCESS);
8789 }
8790 
8791 /*@
8792   DMPlexCreateCellNumbering - Get a global cell numbering for all cells on this process
8793 
8794   Input Parameters:
8795 + dm         - The `DMPLEX` object
8796 - includeAll - Whether to include all cells, or just the simplex and box cells
8797 
8798   Output Parameter:
8799 . globalCellNumbers - Global cell numbers for all cells on this process
8800 
8801   Level: developer
8802 
8803 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`
8804 @*/
8805 PetscErrorCode DMPlexCreateCellNumbering(DM dm, PetscBool includeAll, IS *globalCellNumbers)
8806 {
8807   PetscInt cellHeight, cStart, cEnd;
8808 
8809   PetscFunctionBegin;
8810   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8811   if (includeAll) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8812   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8813   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8814   PetscFunctionReturn(PETSC_SUCCESS);
8815 }
8816 
8817 /*@
8818   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8819 
8820   Input Parameter:
8821 . dm - The `DMPLEX` object
8822 
8823   Output Parameter:
8824 . globalCellNumbers - Global cell numbers for all cells on this process
8825 
8826   Level: developer
8827 
8828 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateCellNumbering()`, `DMPlexGetVertexNumbering()`
8829 @*/
8830 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8831 {
8832   DM_Plex *mesh = (DM_Plex *)dm->data;
8833 
8834   PetscFunctionBegin;
8835   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8836   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8837   *globalCellNumbers = mesh->globalCellNumbers;
8838   PetscFunctionReturn(PETSC_SUCCESS);
8839 }
8840 
8841 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8842 {
8843   PetscInt vStart, vEnd;
8844 
8845   PetscFunctionBegin;
8846   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8847   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8848   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8849   PetscFunctionReturn(PETSC_SUCCESS);
8850 }
8851 
8852 /*@
8853   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8854 
8855   Input Parameter:
8856 . dm - The `DMPLEX` object
8857 
8858   Output Parameter:
8859 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8860 
8861   Level: developer
8862 
8863 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8864 @*/
8865 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8866 {
8867   DM_Plex *mesh = (DM_Plex *)dm->data;
8868 
8869   PetscFunctionBegin;
8870   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8871   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8872   *globalVertexNumbers = mesh->globalVertexNumbers;
8873   PetscFunctionReturn(PETSC_SUCCESS);
8874 }
8875 
8876 /*@
8877   DMPlexCreatePointNumbering - Create a global numbering for all points.
8878 
8879   Collective
8880 
8881   Input Parameter:
8882 . dm - The `DMPLEX` object
8883 
8884   Output Parameter:
8885 . globalPointNumbers - Global numbers for all points on this process
8886 
8887   Level: developer
8888 
8889   Notes:
8890   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8891   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8892   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8893   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8894 
8895   The partitioned mesh is
8896   ```
8897   (2)--0--(3)--1--(4)    (1)--0--(2)
8898   ```
8899   and its global numbering is
8900   ```
8901   (3)--0--(4)--1--(5)--2--(6)
8902   ```
8903   Then the global numbering is provided as
8904   ```
8905   [0] Number of indices in set 5
8906   [0] 0 0
8907   [0] 1 1
8908   [0] 2 3
8909   [0] 3 4
8910   [0] 4 -6
8911   [1] Number of indices in set 3
8912   [1] 0 2
8913   [1] 1 5
8914   [1] 2 6
8915   ```
8916 
8917 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8918 @*/
8919 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8920 {
8921   IS        nums[4];
8922   PetscInt  depths[4], gdepths[4], starts[4];
8923   PetscInt  depth, d, shift = 0;
8924   PetscBool empty = PETSC_FALSE;
8925 
8926   PetscFunctionBegin;
8927   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8928   PetscCall(DMPlexGetDepth(dm, &depth));
8929   // For unstratified meshes use dim instead of depth
8930   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8931   // If any stratum is empty, we must mark all empty
8932   for (d = 0; d <= depth; ++d) {
8933     PetscInt end;
8934 
8935     depths[d] = depth - d;
8936     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8937     if (!(starts[d] - end)) empty = PETSC_TRUE;
8938   }
8939   if (empty)
8940     for (d = 0; d <= depth; ++d) {
8941       depths[d] = -1;
8942       starts[d] = -1;
8943     }
8944   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8945   PetscCall(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8946   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]);
8947   // Note here that 'shift' is collective, so that the numbering is stratified by depth
8948   for (d = 0; d <= depth; ++d) {
8949     PetscInt pStart, pEnd, gsize;
8950 
8951     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8952     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8953     shift += gsize;
8954   }
8955   PetscCall(ISConcatenate(PETSC_COMM_SELF, depth + 1, nums, globalPointNumbers));
8956   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8957   PetscFunctionReturn(PETSC_SUCCESS);
8958 }
8959 
8960 /*@
8961   DMPlexCreateEdgeNumbering - Create a global numbering for edges.
8962 
8963   Collective
8964 
8965   Input Parameter:
8966 . dm - The `DMPLEX` object
8967 
8968   Output Parameter:
8969 . globalEdgeNumbers - Global numbers for all edges on this process
8970 
8971   Level: developer
8972 
8973   Notes:
8974   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).
8975 
8976 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`, `DMPlexCreatePointNumbering()`
8977 @*/
8978 PetscErrorCode DMPlexCreateEdgeNumbering(DM dm, IS *globalEdgeNumbers)
8979 {
8980   PetscSF  sf;
8981   PetscInt eStart, eEnd;
8982 
8983   PetscFunctionBegin;
8984   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8985   PetscCall(DMGetPointSF(dm, &sf));
8986   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
8987   PetscCall(DMPlexCreateNumbering_Plex(dm, eStart, eEnd, 0, NULL, sf, globalEdgeNumbers));
8988   PetscFunctionReturn(PETSC_SUCCESS);
8989 }
8990 
8991 /*@
8992   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8993 
8994   Input Parameter:
8995 . dm - The `DMPLEX` object
8996 
8997   Output Parameter:
8998 . ranks - The rank field
8999 
9000   Options Database Key:
9001 . -dm_partition_view - Adds the rank field into the `DM` output from `-dm_view` using the same viewer
9002 
9003   Level: intermediate
9004 
9005 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
9006 @*/
9007 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
9008 {
9009   DM             rdm;
9010   PetscFE        fe;
9011   PetscScalar   *r;
9012   PetscMPIInt    rank;
9013   DMPolytopeType ct;
9014   PetscInt       dim, cStart, cEnd, c;
9015   PetscBool      simplex;
9016 
9017   PetscFunctionBeginUser;
9018   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9019   PetscAssertPointer(ranks, 2);
9020   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
9021   PetscCall(DMClone(dm, &rdm));
9022   PetscCall(DMGetDimension(rdm, &dim));
9023   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
9024   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
9025   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
9026   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
9027   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
9028   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
9029   PetscCall(PetscFEDestroy(&fe));
9030   PetscCall(DMCreateDS(rdm));
9031   PetscCall(DMCreateGlobalVector(rdm, ranks));
9032   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
9033   PetscCall(VecGetArray(*ranks, &r));
9034   for (c = cStart; c < cEnd; ++c) {
9035     PetscScalar *lr;
9036 
9037     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
9038     if (lr) *lr = rank;
9039   }
9040   PetscCall(VecRestoreArray(*ranks, &r));
9041   PetscCall(DMDestroy(&rdm));
9042   PetscFunctionReturn(PETSC_SUCCESS);
9043 }
9044 
9045 /*@
9046   DMPlexCreateLabelField - Create a field whose value is the label value for that point
9047 
9048   Input Parameters:
9049 + dm    - The `DMPLEX`
9050 - label - The `DMLabel`
9051 
9052   Output Parameter:
9053 . val - The label value field
9054 
9055   Options Database Key:
9056 . -dm_label_view - Adds the label value field into the `DM` output from `-dm_view` using the same viewer
9057 
9058   Level: intermediate
9059 
9060 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
9061 @*/
9062 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
9063 {
9064   DM             rdm, plex;
9065   Vec            lval;
9066   PetscSection   section;
9067   PetscFE        fe;
9068   PetscScalar   *v;
9069   PetscInt       dim, pStart, pEnd, p, cStart;
9070   DMPolytopeType ct;
9071   char           name[PETSC_MAX_PATH_LEN];
9072   const char    *lname, *prefix;
9073 
9074   PetscFunctionBeginUser;
9075   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9076   PetscAssertPointer(label, 2);
9077   PetscAssertPointer(val, 3);
9078   PetscCall(DMClone(dm, &rdm));
9079   PetscCall(DMConvert(rdm, DMPLEX, &plex));
9080   PetscCall(DMPlexGetHeightStratum(plex, 0, &cStart, NULL));
9081   PetscCall(DMPlexGetCellType(plex, cStart, &ct));
9082   PetscCall(DMDestroy(&plex));
9083   PetscCall(DMGetDimension(rdm, &dim));
9084   PetscCall(DMGetOptionsPrefix(dm, &prefix));
9085   PetscCall(PetscObjectGetName((PetscObject)label, &lname));
9086   PetscCall(PetscSNPrintf(name, sizeof(name), "%s%s_", prefix ? prefix : "", lname));
9087   PetscCall(PetscFECreateByCell(PETSC_COMM_SELF, dim, 1, ct, name, -1, &fe));
9088   PetscCall(PetscObjectSetName((PetscObject)fe, ""));
9089   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
9090   PetscCall(PetscFEDestroy(&fe));
9091   PetscCall(DMCreateDS(rdm));
9092   PetscCall(DMCreateGlobalVector(rdm, val));
9093   PetscCall(DMCreateLocalVector(rdm, &lval));
9094   PetscCall(PetscObjectSetName((PetscObject)*val, lname));
9095   PetscCall(DMGetLocalSection(rdm, &section));
9096   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
9097   PetscCall(VecGetArray(lval, &v));
9098   for (p = pStart; p < pEnd; ++p) {
9099     PetscInt cval, dof, off;
9100 
9101     PetscCall(PetscSectionGetDof(section, p, &dof));
9102     if (!dof) continue;
9103     PetscCall(DMLabelGetValue(label, p, &cval));
9104     PetscCall(PetscSectionGetOffset(section, p, &off));
9105     for (PetscInt d = 0; d < dof; d++) v[off + d] = cval;
9106   }
9107   PetscCall(VecRestoreArray(lval, &v));
9108   PetscCall(DMLocalToGlobal(rdm, lval, INSERT_VALUES, *val));
9109   PetscCall(VecDestroy(&lval));
9110   PetscCall(DMDestroy(&rdm));
9111   PetscFunctionReturn(PETSC_SUCCESS);
9112 }
9113 
9114 /*@
9115   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
9116 
9117   Input Parameter:
9118 . dm - The `DMPLEX` object
9119 
9120   Level: developer
9121 
9122   Notes:
9123   This is a useful diagnostic when creating meshes programmatically.
9124 
9125   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9126 
9127 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9128 @*/
9129 PetscErrorCode DMPlexCheckSymmetry(DM dm)
9130 {
9131   PetscSection    coneSection, supportSection;
9132   const PetscInt *cone, *support;
9133   PetscInt        coneSize, c, supportSize, s;
9134   PetscInt        pStart, pEnd, p, pp, csize, ssize;
9135   PetscBool       storagecheck = PETSC_TRUE;
9136 
9137   PetscFunctionBegin;
9138   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9139   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
9140   PetscCall(DMPlexGetConeSection(dm, &coneSection));
9141   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
9142   /* Check that point p is found in the support of its cone points, and vice versa */
9143   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9144   for (p = pStart; p < pEnd; ++p) {
9145     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
9146     PetscCall(DMPlexGetCone(dm, p, &cone));
9147     for (c = 0; c < coneSize; ++c) {
9148       PetscBool dup = PETSC_FALSE;
9149       PetscInt  d;
9150       for (d = c - 1; d >= 0; --d) {
9151         if (cone[c] == cone[d]) {
9152           dup = PETSC_TRUE;
9153           break;
9154         }
9155       }
9156       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
9157       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
9158       for (s = 0; s < supportSize; ++s) {
9159         if (support[s] == p) break;
9160       }
9161       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
9162         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
9163         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
9164         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9165         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
9166         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
9167         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9168         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]);
9169         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
9170       }
9171     }
9172     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
9173     if (p != pp) {
9174       storagecheck = PETSC_FALSE;
9175       continue;
9176     }
9177     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
9178     PetscCall(DMPlexGetSupport(dm, p, &support));
9179     for (s = 0; s < supportSize; ++s) {
9180       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
9181       PetscCall(DMPlexGetCone(dm, support[s], &cone));
9182       for (c = 0; c < coneSize; ++c) {
9183         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
9184         if (cone[c] != pp) {
9185           c = 0;
9186           break;
9187         }
9188         if (cone[c] == p) break;
9189       }
9190       if (c >= coneSize) {
9191         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
9192         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
9193         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9194         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
9195         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
9196         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9197         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
9198       }
9199     }
9200   }
9201   if (storagecheck) {
9202     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
9203     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
9204     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
9205   }
9206   PetscFunctionReturn(PETSC_SUCCESS);
9207 }
9208 
9209 /*
9210   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.
9211 */
9212 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
9213 {
9214   DMPolytopeType  cct;
9215   PetscInt        ptpoints[4];
9216   const PetscInt *cone, *ccone, *ptcone;
9217   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
9218 
9219   PetscFunctionBegin;
9220   *unsplit = 0;
9221   switch (ct) {
9222   case DM_POLYTOPE_POINT_PRISM_TENSOR:
9223     ptpoints[npt++] = c;
9224     break;
9225   case DM_POLYTOPE_SEG_PRISM_TENSOR:
9226     PetscCall(DMPlexGetCone(dm, c, &cone));
9227     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9228     for (cp = 0; cp < coneSize; ++cp) {
9229       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
9230       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
9231     }
9232     break;
9233   case DM_POLYTOPE_TRI_PRISM_TENSOR:
9234   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9235     PetscCall(DMPlexGetCone(dm, c, &cone));
9236     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9237     for (cp = 0; cp < coneSize; ++cp) {
9238       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
9239       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
9240       for (ccp = 0; ccp < cconeSize; ++ccp) {
9241         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
9242         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
9243           PetscInt p;
9244           for (p = 0; p < npt; ++p)
9245             if (ptpoints[p] == ccone[ccp]) break;
9246           if (p == npt) ptpoints[npt++] = ccone[ccp];
9247         }
9248       }
9249     }
9250     break;
9251   default:
9252     break;
9253   }
9254   for (pt = 0; pt < npt; ++pt) {
9255     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
9256     if (ptcone[0] == ptcone[1]) ++(*unsplit);
9257   }
9258   PetscFunctionReturn(PETSC_SUCCESS);
9259 }
9260 
9261 /*@
9262   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
9263 
9264   Input Parameters:
9265 + dm         - The `DMPLEX` object
9266 - cellHeight - Normally 0
9267 
9268   Level: developer
9269 
9270   Notes:
9271   This is a useful diagnostic when creating meshes programmatically.
9272   Currently applicable only to homogeneous simplex or tensor meshes.
9273 
9274   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9275 
9276 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9277 @*/
9278 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
9279 {
9280   DMPlexInterpolatedFlag interp;
9281   DMPolytopeType         ct;
9282   PetscInt               vStart, vEnd, cStart, cEnd, c;
9283 
9284   PetscFunctionBegin;
9285   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9286   PetscCall(DMPlexIsInterpolated(dm, &interp));
9287   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9288   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9289   for (c = cStart; c < cEnd; ++c) {
9290     PetscInt *closure = NULL;
9291     PetscInt  coneSize, closureSize, cl, Nv = 0;
9292 
9293     PetscCall(DMPlexGetCellType(dm, c, &ct));
9294     if (ct == DM_POLYTOPE_UNKNOWN) continue;
9295     if (interp == DMPLEX_INTERPOLATED_FULL) {
9296       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9297       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));
9298     }
9299     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9300     for (cl = 0; cl < closureSize * 2; cl += 2) {
9301       const PetscInt p = closure[cl];
9302       if ((p >= vStart) && (p < vEnd)) ++Nv;
9303     }
9304     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9305     /* Special Case: Tensor faces with identified vertices */
9306     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
9307       PetscInt unsplit;
9308 
9309       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9310       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
9311     }
9312     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));
9313   }
9314   PetscFunctionReturn(PETSC_SUCCESS);
9315 }
9316 
9317 /*@
9318   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
9319 
9320   Collective
9321 
9322   Input Parameters:
9323 + dm         - The `DMPLEX` object
9324 - cellHeight - Normally 0
9325 
9326   Level: developer
9327 
9328   Notes:
9329   This is a useful diagnostic when creating meshes programmatically.
9330   This routine is only relevant for meshes that are fully interpolated across all ranks.
9331   It will error out if a partially interpolated mesh is given on some rank.
9332   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
9333 
9334   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9335 
9336 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
9337 @*/
9338 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
9339 {
9340   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
9341   DMPlexInterpolatedFlag interpEnum;
9342 
9343   PetscFunctionBegin;
9344   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9345   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
9346   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(PETSC_SUCCESS);
9347   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
9348     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported"));
9349     PetscFunctionReturn(PETSC_SUCCESS);
9350   }
9351 
9352   PetscCall(DMGetDimension(dm, &dim));
9353   PetscCall(DMPlexGetDepth(dm, &depth));
9354   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9355   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
9356     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
9357     for (c = cStart; c < cEnd; ++c) {
9358       const PetscInt       *cone, *ornt, *faceSizes, *faces;
9359       const DMPolytopeType *faceTypes;
9360       DMPolytopeType        ct;
9361       PetscInt              numFaces, coneSize, f;
9362       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
9363 
9364       PetscCall(DMPlexGetCellType(dm, c, &ct));
9365       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9366       if (unsplit) continue;
9367       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9368       PetscCall(DMPlexGetCone(dm, c, &cone));
9369       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
9370       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9371       for (cl = 0; cl < closureSize * 2; cl += 2) {
9372         const PetscInt p = closure[cl];
9373         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
9374       }
9375       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9376       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);
9377       for (f = 0; f < numFaces; ++f) {
9378         DMPolytopeType fct;
9379         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
9380 
9381         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
9382         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
9383         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
9384           const PetscInt p = fclosure[cl];
9385           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
9386         }
9387         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]);
9388         for (v = 0; v < fnumCorners; ++v) {
9389           if (fclosure[v] != faces[fOff + v]) {
9390             PetscInt v1;
9391 
9392             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
9393             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
9394             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
9395             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
9396             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9397             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]);
9398           }
9399         }
9400         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
9401         fOff += faceSizes[f];
9402       }
9403       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9404       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9405     }
9406   }
9407   PetscFunctionReturn(PETSC_SUCCESS);
9408 }
9409 
9410 /*@
9411   DMPlexCheckGeometry - Check the geometry of mesh cells
9412 
9413   Input Parameter:
9414 . dm - The `DMPLEX` object
9415 
9416   Level: developer
9417 
9418   Notes:
9419   This is a useful diagnostic when creating meshes programmatically.
9420 
9421   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9422 
9423 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9424 @*/
9425 PetscErrorCode DMPlexCheckGeometry(DM dm)
9426 {
9427   Vec       coordinates;
9428   PetscReal detJ, J[9], refVol = 1.0;
9429   PetscReal vol;
9430   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
9431 
9432   PetscFunctionBegin;
9433   PetscCall(DMGetDimension(dm, &dim));
9434   PetscCall(DMGetCoordinateDim(dm, &dE));
9435   if (dim != dE) PetscFunctionReturn(PETSC_SUCCESS);
9436   PetscCall(DMPlexGetDepth(dm, &depth));
9437   for (d = 0; d < dim; ++d) refVol *= 2.0;
9438   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9439   /* Make sure local coordinates are created, because that step is collective */
9440   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
9441   if (!coordinates) PetscFunctionReturn(PETSC_SUCCESS);
9442   for (c = cStart; c < cEnd; ++c) {
9443     DMPolytopeType ct;
9444     PetscInt       unsplit;
9445     PetscBool      ignoreZeroVol = PETSC_FALSE;
9446 
9447     PetscCall(DMPlexGetCellType(dm, c, &ct));
9448     switch (ct) {
9449     case DM_POLYTOPE_SEG_PRISM_TENSOR:
9450     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9451     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9452       ignoreZeroVol = PETSC_TRUE;
9453       break;
9454     default:
9455       break;
9456     }
9457     switch (ct) {
9458     case DM_POLYTOPE_TRI_PRISM:
9459     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9460     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9461     case DM_POLYTOPE_PYRAMID:
9462       continue;
9463     default:
9464       break;
9465     }
9466     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9467     if (unsplit) continue;
9468     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
9469     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);
9470     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
9471     /* This should work with periodicity since DG coordinates should be used */
9472     if (depth > 1) {
9473       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
9474       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);
9475       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
9476     }
9477   }
9478   PetscFunctionReturn(PETSC_SUCCESS);
9479 }
9480 
9481 /*@
9482   DMPlexCheckPointSF - Check that several necessary conditions are met for the point `PetscSF` of this plex.
9483 
9484   Collective
9485 
9486   Input Parameters:
9487 + dm              - The `DMPLEX` object
9488 . pointSF         - The `PetscSF`, or `NULL` for `PointSF` attached to `DM`
9489 - allowExtraRoots - Flag to allow extra points not present in the `DM`
9490 
9491   Level: developer
9492 
9493   Notes:
9494   This is mainly intended for debugging/testing purposes.
9495 
9496   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9497 
9498   Extra roots can come from periodic cuts, where additional points appear on the boundary
9499 
9500 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
9501 @*/
9502 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
9503 {
9504   PetscInt           l, nleaves, nroots, overlap;
9505   const PetscInt    *locals;
9506   const PetscSFNode *remotes;
9507   PetscBool          distributed;
9508   MPI_Comm           comm;
9509   PetscMPIInt        rank;
9510 
9511   PetscFunctionBegin;
9512   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9513   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
9514   else pointSF = dm->sf;
9515   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9516   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
9517   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9518   {
9519     PetscMPIInt mpiFlag;
9520 
9521     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
9522     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
9523   }
9524   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
9525   PetscCall(DMPlexIsDistributed(dm, &distributed));
9526   if (!distributed) {
9527     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);
9528     PetscFunctionReturn(PETSC_SUCCESS);
9529   }
9530   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);
9531   PetscCall(DMPlexGetOverlap(dm, &overlap));
9532 
9533   /* Check SF graph is compatible with DMPlex chart */
9534   {
9535     PetscInt pStart, pEnd, maxLeaf;
9536 
9537     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9538     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9539     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9540     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9541   }
9542 
9543   /* Check Point SF has no local points referenced */
9544   for (l = 0; l < nleaves; l++) {
9545     PetscAssert(remotes[l].rank != (PetscInt)rank, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains local point %" PetscInt_FMT " <- (%" PetscInt_FMT ",%" PetscInt_FMT ")", locals ? locals[l] : l, remotes[l].rank, remotes[l].index);
9546   }
9547 
9548   /* Check there are no cells in interface */
9549   if (!overlap) {
9550     PetscInt cellHeight, cStart, cEnd;
9551 
9552     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9553     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9554     for (l = 0; l < nleaves; ++l) {
9555       const PetscInt point = locals ? locals[l] : l;
9556 
9557       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9558     }
9559   }
9560 
9561   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9562   {
9563     const PetscInt *rootdegree;
9564 
9565     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9566     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9567     for (l = 0; l < nleaves; ++l) {
9568       const PetscInt  point = locals ? locals[l] : l;
9569       const PetscInt *cone;
9570       PetscInt        coneSize, c, idx;
9571 
9572       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9573       PetscCall(DMPlexGetCone(dm, point, &cone));
9574       for (c = 0; c < coneSize; ++c) {
9575         if (!rootdegree[cone[c]]) {
9576           if (locals) {
9577             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9578           } else {
9579             idx = (cone[c] < nleaves) ? cone[c] : -1;
9580           }
9581           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9582         }
9583       }
9584     }
9585   }
9586   PetscFunctionReturn(PETSC_SUCCESS);
9587 }
9588 
9589 /*@
9590   DMPlexCheckOrphanVertices - Check that no vertices are disconnected from the mesh, unless the mesh only consists of disconnected vertices.
9591 
9592   Collective
9593 
9594   Input Parameter:
9595 . dm - The `DMPLEX` object
9596 
9597   Level: developer
9598 
9599   Notes:
9600   This is mainly intended for debugging/testing purposes.
9601 
9602   Other cell types which are disconnected would be caught by the symmetry and face checks.
9603 
9604   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9605 
9606 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheck()`, `DMSetFromOptions()`
9607 @*/
9608 PetscErrorCode DMPlexCheckOrphanVertices(DM dm)
9609 {
9610   PetscInt pStart, pEnd, vStart, vEnd;
9611 
9612   PetscFunctionBegin;
9613   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9614   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9615   if (pStart == vStart && pEnd == vEnd) PetscFunctionReturn(PETSC_SUCCESS);
9616   for (PetscInt v = vStart; v < vEnd; ++v) {
9617     PetscInt suppSize;
9618 
9619     PetscCall(DMPlexGetSupportSize(dm, v, &suppSize));
9620     PetscCheck(suppSize, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Vertex %" PetscInt_FMT " is disconnected from the mesh", v);
9621   }
9622   PetscFunctionReturn(PETSC_SUCCESS);
9623 }
9624 
9625 /*@
9626   DMPlexCheck - Perform various checks of `DMPLEX` sanity
9627 
9628   Input Parameter:
9629 . dm - The `DMPLEX` object
9630 
9631   Level: developer
9632 
9633   Notes:
9634   This is a useful diagnostic when creating meshes programmatically.
9635 
9636   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9637 
9638   Currently does not include `DMPlexCheckCellShape()`.
9639 
9640 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9641 @*/
9642 PetscErrorCode DMPlexCheck(DM dm)
9643 {
9644   PetscInt cellHeight;
9645 
9646   PetscFunctionBegin;
9647   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9648   PetscCall(DMPlexCheckSymmetry(dm));
9649   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9650   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9651   PetscCall(DMPlexCheckGeometry(dm));
9652   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9653   PetscCall(DMPlexCheckInterfaceCones(dm));
9654   PetscCall(DMPlexCheckOrphanVertices(dm));
9655   PetscFunctionReturn(PETSC_SUCCESS);
9656 }
9657 
9658 typedef struct cell_stats {
9659   PetscReal min, max, sum, squaresum;
9660   PetscInt  count;
9661 } cell_stats_t;
9662 
9663 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9664 {
9665   PetscInt i, N = *len;
9666 
9667   for (i = 0; i < N; i++) {
9668     cell_stats_t *A = (cell_stats_t *)a;
9669     cell_stats_t *B = (cell_stats_t *)b;
9670 
9671     B->min = PetscMin(A->min, B->min);
9672     B->max = PetscMax(A->max, B->max);
9673     B->sum += A->sum;
9674     B->squaresum += A->squaresum;
9675     B->count += A->count;
9676   }
9677 }
9678 
9679 /*@
9680   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9681 
9682   Collective
9683 
9684   Input Parameters:
9685 + dm        - The `DMPLEX` object
9686 . output    - If true, statistics will be displayed on `stdout`
9687 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9688 
9689   Level: developer
9690 
9691   Notes:
9692   This is mainly intended for debugging/testing purposes.
9693 
9694   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9695 
9696 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9697 @*/
9698 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9699 {
9700   DM           dmCoarse;
9701   cell_stats_t stats, globalStats;
9702   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9703   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9704   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9705   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9706   PetscMPIInt  rank, size;
9707 
9708   PetscFunctionBegin;
9709   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9710   stats.min = PETSC_MAX_REAL;
9711   stats.max = PETSC_MIN_REAL;
9712   stats.sum = stats.squaresum = 0.;
9713   stats.count                 = 0;
9714 
9715   PetscCallMPI(MPI_Comm_size(comm, &size));
9716   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9717   PetscCall(DMGetCoordinateDim(dm, &cdim));
9718   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9719   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9720   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9721   for (c = cStart; c < cEnd; c++) {
9722     PetscInt  i;
9723     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9724 
9725     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9726     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9727     for (i = 0; i < PetscSqr(cdim); ++i) {
9728       frobJ += J[i] * J[i];
9729       frobInvJ += invJ[i] * invJ[i];
9730     }
9731     cond2 = frobJ * frobInvJ;
9732     cond  = PetscSqrtReal(cond2);
9733 
9734     stats.min = PetscMin(stats.min, cond);
9735     stats.max = PetscMax(stats.max, cond);
9736     stats.sum += cond;
9737     stats.squaresum += cond2;
9738     stats.count++;
9739     if (output && cond > limit) {
9740       PetscSection coordSection;
9741       Vec          coordsLocal;
9742       PetscScalar *coords = NULL;
9743       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9744 
9745       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9746       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9747       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9748       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9749       for (i = 0; i < Nv / cdim; ++i) {
9750         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9751         for (d = 0; d < cdim; ++d) {
9752           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9753           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9754         }
9755         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9756       }
9757       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9758       for (cl = 0; cl < clSize * 2; cl += 2) {
9759         const PetscInt edge = closure[cl];
9760 
9761         if ((edge >= eStart) && (edge < eEnd)) {
9762           PetscReal len;
9763 
9764           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9765           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9766         }
9767       }
9768       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9769       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9770     }
9771   }
9772   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9773 
9774   if (size > 1) {
9775     PetscMPIInt  blockLengths[2] = {4, 1};
9776     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9777     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9778     MPI_Op       statReduce;
9779 
9780     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9781     PetscCallMPI(MPI_Type_commit(&statType));
9782     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9783     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9784     PetscCallMPI(MPI_Op_free(&statReduce));
9785     PetscCallMPI(MPI_Type_free(&statType));
9786   } else {
9787     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9788   }
9789   if (rank == 0) {
9790     count = globalStats.count;
9791     min   = globalStats.min;
9792     max   = globalStats.max;
9793     mean  = globalStats.sum / globalStats.count;
9794     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9795   }
9796 
9797   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));
9798   PetscCall(PetscFree2(J, invJ));
9799 
9800   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9801   if (dmCoarse) {
9802     PetscBool isplex;
9803 
9804     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9805     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9806   }
9807   PetscFunctionReturn(PETSC_SUCCESS);
9808 }
9809 
9810 /*@
9811   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9812   orthogonal quality below given tolerance.
9813 
9814   Collective
9815 
9816   Input Parameters:
9817 + dm   - The `DMPLEX` object
9818 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9819 - atol - [0, 1] Absolute tolerance for tagging cells.
9820 
9821   Output Parameters:
9822 + OrthQual      - `Vec` containing orthogonal quality per cell
9823 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9824 
9825   Options Database Keys:
9826 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9827 - -dm_plex_orthogonal_quality_vec_view   - view OrthQual vector.
9828 
9829   Level: intermediate
9830 
9831   Notes:
9832   Orthogonal quality is given by the following formula\:
9833 
9834   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9835 
9836   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
9837   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9838   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9839   calculating the cosine of the angle between these vectors.
9840 
9841   Orthogonal quality ranges from 1 (best) to 0 (worst).
9842 
9843   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9844   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9845 
9846   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9847 
9848 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9849 @*/
9850 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9851 {
9852   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9853   PetscInt              *idx;
9854   PetscScalar           *oqVals;
9855   const PetscScalar     *cellGeomArr, *faceGeomArr;
9856   PetscReal             *ci, *fi, *Ai;
9857   MPI_Comm               comm;
9858   Vec                    cellgeom, facegeom;
9859   DM                     dmFace, dmCell;
9860   IS                     glob;
9861   ISLocalToGlobalMapping ltog;
9862   PetscViewer            vwr;
9863 
9864   PetscFunctionBegin;
9865   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9866   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9867   PetscAssertPointer(OrthQual, 4);
9868   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9869   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9870   PetscCall(DMGetDimension(dm, &nc));
9871   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9872   {
9873     DMPlexInterpolatedFlag interpFlag;
9874 
9875     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9876     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9877       PetscMPIInt rank;
9878 
9879       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9880       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9881     }
9882   }
9883   if (OrthQualLabel) {
9884     PetscAssertPointer(OrthQualLabel, 5);
9885     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9886     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9887   } else {
9888     *OrthQualLabel = NULL;
9889   }
9890   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9891   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9892   PetscCall(DMPlexCreateCellNumbering(dm, PETSC_TRUE, &glob));
9893   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9894   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9895   PetscCall(VecCreate(comm, OrthQual));
9896   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9897   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9898   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9899   PetscCall(VecSetUp(*OrthQual));
9900   PetscCall(ISDestroy(&glob));
9901   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9902   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9903   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9904   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9905   PetscCall(VecGetDM(cellgeom, &dmCell));
9906   PetscCall(VecGetDM(facegeom, &dmFace));
9907   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9908   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
9909     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9910     PetscInt         cellarr[2], *adj = NULL;
9911     PetscScalar     *cArr, *fArr;
9912     PetscReal        minvalc = 1.0, minvalf = 1.0;
9913     PetscFVCellGeom *cg;
9914 
9915     idx[cellIter] = cell - cStart;
9916     cellarr[0]    = cell;
9917     /* Make indexing into cellGeom easier */
9918     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9919     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9920     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9921     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9922     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
9923       PetscInt         i;
9924       const PetscInt   neigh  = adj[cellneigh];
9925       PetscReal        normci = 0, normfi = 0, normai = 0;
9926       PetscFVCellGeom *cgneigh;
9927       PetscFVFaceGeom *fg;
9928 
9929       /* Don't count ourselves in the neighbor list */
9930       if (neigh == cell) continue;
9931       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9932       cellarr[1] = neigh;
9933       {
9934         PetscInt        numcovpts;
9935         const PetscInt *covpts;
9936 
9937         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9938         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9939         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9940       }
9941 
9942       /* Compute c_i, f_i and their norms */
9943       for (i = 0; i < nc; i++) {
9944         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9945         fi[i] = fg->centroid[i] - cg->centroid[i];
9946         Ai[i] = fg->normal[i];
9947         normci += PetscPowReal(ci[i], 2);
9948         normfi += PetscPowReal(fi[i], 2);
9949         normai += PetscPowReal(Ai[i], 2);
9950       }
9951       normci = PetscSqrtReal(normci);
9952       normfi = PetscSqrtReal(normfi);
9953       normai = PetscSqrtReal(normai);
9954 
9955       /* Normalize and compute for each face-cell-normal pair */
9956       for (i = 0; i < nc; i++) {
9957         ci[i] = ci[i] / normci;
9958         fi[i] = fi[i] / normfi;
9959         Ai[i] = Ai[i] / normai;
9960         /* PetscAbs because I don't know if normals are guaranteed to point out */
9961         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
9962         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
9963       }
9964       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
9965       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
9966     }
9967     PetscCall(PetscFree(adj));
9968     PetscCall(PetscFree2(cArr, fArr));
9969     /* Defer to cell if they're equal */
9970     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9971     if (OrthQualLabel) {
9972       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9973     }
9974   }
9975   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
9976   PetscCall(VecAssemblyBegin(*OrthQual));
9977   PetscCall(VecAssemblyEnd(*OrthQual));
9978   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9979   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9980   PetscCall(PetscOptionsCreateViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9981   if (OrthQualLabel) {
9982     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9983   }
9984   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9985   PetscCall(PetscViewerDestroy(&vwr));
9986   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9987   PetscFunctionReturn(PETSC_SUCCESS);
9988 }
9989 
9990 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9991  * interpolator construction */
9992 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9993 {
9994   PetscSection section, newSection, gsection;
9995   PetscSF      sf;
9996   PetscBool    hasConstraints, ghasConstraints;
9997 
9998   PetscFunctionBegin;
9999   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10000   PetscAssertPointer(odm, 2);
10001   PetscCall(DMGetLocalSection(dm, &section));
10002   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
10003   PetscCall(MPIU_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
10004   if (!ghasConstraints) {
10005     PetscCall(PetscObjectReference((PetscObject)dm));
10006     *odm = dm;
10007     PetscFunctionReturn(PETSC_SUCCESS);
10008   }
10009   PetscCall(DMClone(dm, odm));
10010   PetscCall(DMCopyFields(dm, PETSC_DETERMINE, PETSC_DETERMINE, *odm));
10011   PetscCall(DMGetLocalSection(*odm, &newSection));
10012   PetscCall(DMGetPointSF(*odm, &sf));
10013   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_TRUE, PETSC_FALSE, &gsection));
10014   PetscCall(DMSetGlobalSection(*odm, gsection));
10015   PetscCall(PetscSectionDestroy(&gsection));
10016   PetscFunctionReturn(PETSC_SUCCESS);
10017 }
10018 
10019 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
10020 {
10021   DM        dmco, dmfo;
10022   Mat       interpo;
10023   Vec       rscale;
10024   Vec       cglobalo, clocal;
10025   Vec       fglobal, fglobalo, flocal;
10026   PetscBool regular;
10027 
10028   PetscFunctionBegin;
10029   PetscCall(DMGetFullDM(dmc, &dmco));
10030   PetscCall(DMGetFullDM(dmf, &dmfo));
10031   PetscCall(DMSetCoarseDM(dmfo, dmco));
10032   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
10033   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
10034   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
10035   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
10036   PetscCall(DMCreateLocalVector(dmc, &clocal));
10037   PetscCall(VecSet(cglobalo, 0.));
10038   PetscCall(VecSet(clocal, 0.));
10039   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
10040   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
10041   PetscCall(DMCreateLocalVector(dmf, &flocal));
10042   PetscCall(VecSet(fglobal, 0.));
10043   PetscCall(VecSet(fglobalo, 0.));
10044   PetscCall(VecSet(flocal, 0.));
10045   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
10046   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
10047   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
10048   PetscCall(MatMult(interpo, cglobalo, fglobalo));
10049   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
10050   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
10051   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
10052   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
10053   *shift = fglobal;
10054   PetscCall(VecDestroy(&flocal));
10055   PetscCall(VecDestroy(&fglobalo));
10056   PetscCall(VecDestroy(&clocal));
10057   PetscCall(VecDestroy(&cglobalo));
10058   PetscCall(VecDestroy(&rscale));
10059   PetscCall(MatDestroy(&interpo));
10060   PetscCall(DMDestroy(&dmfo));
10061   PetscCall(DMDestroy(&dmco));
10062   PetscFunctionReturn(PETSC_SUCCESS);
10063 }
10064 
10065 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
10066 {
10067   PetscObject shifto;
10068   Vec         shift;
10069 
10070   PetscFunctionBegin;
10071   if (!interp) {
10072     Vec rscale;
10073 
10074     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
10075     PetscCall(VecDestroy(&rscale));
10076   } else {
10077     PetscCall(PetscObjectReference((PetscObject)interp));
10078   }
10079   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
10080   if (!shifto) {
10081     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
10082     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
10083     shifto = (PetscObject)shift;
10084     PetscCall(VecDestroy(&shift));
10085   }
10086   shift = (Vec)shifto;
10087   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
10088   PetscCall(VecAXPY(fineSol, 1.0, shift));
10089   PetscCall(MatDestroy(&interp));
10090   PetscFunctionReturn(PETSC_SUCCESS);
10091 }
10092 
10093 /* Pointwise interpolation
10094      Just code FEM for now
10095      u^f = I u^c
10096      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
10097      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
10098      I_{ij} = psi^f_i phi^c_j
10099 */
10100 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
10101 {
10102   PetscSection gsc, gsf;
10103   PetscInt     m, n;
10104   void        *ctx;
10105   DM           cdm;
10106   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
10107 
10108   PetscFunctionBegin;
10109   PetscCall(DMGetGlobalSection(dmFine, &gsf));
10110   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10111   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10112   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10113 
10114   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
10115   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
10116   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10117   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
10118   PetscCall(DMGetApplicationContext(dmFine, &ctx));
10119 
10120   PetscCall(DMGetCoarseDM(dmFine, &cdm));
10121   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10122   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
10123   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
10124   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
10125   if (scaling) {
10126     /* Use naive scaling */
10127     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
10128   }
10129   PetscFunctionReturn(PETSC_SUCCESS);
10130 }
10131 
10132 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
10133 {
10134   VecScatter ctx;
10135 
10136   PetscFunctionBegin;
10137   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
10138   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
10139   PetscCall(VecScatterDestroy(&ctx));
10140   PetscFunctionReturn(PETSC_SUCCESS);
10141 }
10142 
10143 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[])
10144 {
10145   const PetscInt f  = (PetscInt)PetscRealPart(constants[numConstants]);
10146   const PetscInt Nc = uOff[f + 1] - uOff[f];
10147   for (PetscInt c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
10148 }
10149 
10150 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *lmass, Vec *mass)
10151 {
10152   DM           dmc;
10153   PetscDS      ds;
10154   Vec          ones, locmass;
10155   IS           cellIS;
10156   PetscFormKey key;
10157   PetscInt     depth;
10158 
10159   PetscFunctionBegin;
10160   PetscCall(DMClone(dm, &dmc));
10161   PetscCall(DMCopyDisc(dm, dmc));
10162   PetscCall(DMGetDS(dmc, &ds));
10163   for (PetscInt f = 0; f < dmc->Nf; ++f) PetscCall(PetscDSSetJacobian(ds, f, f, g0_identity_private, NULL, NULL, NULL));
10164   if (mass) PetscCall(DMCreateGlobalVector(dm, mass));
10165   if (lmass) PetscCall(DMCreateLocalVector(dm, &locmass));
10166   else PetscCall(DMGetLocalVector(dm, &locmass));
10167   PetscCall(DMGetLocalVector(dm, &ones));
10168   PetscCall(DMPlexGetDepth(dm, &depth));
10169   PetscCall(DMGetStratumIS(dm, "depth", depth, &cellIS));
10170   PetscCall(VecSet(locmass, 0.0));
10171   PetscCall(VecSet(ones, 1.0));
10172   key.label = NULL;
10173   key.value = 0;
10174   key.field = 0;
10175   key.part  = 0;
10176   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
10177   PetscCall(ISDestroy(&cellIS));
10178   if (mass) {
10179     PetscCall(DMLocalToGlobalBegin(dm, locmass, ADD_VALUES, *mass));
10180     PetscCall(DMLocalToGlobalEnd(dm, locmass, ADD_VALUES, *mass));
10181   }
10182   PetscCall(DMRestoreLocalVector(dm, &ones));
10183   if (lmass) *lmass = locmass;
10184   else PetscCall(DMRestoreLocalVector(dm, &locmass));
10185   PetscCall(DMDestroy(&dmc));
10186   PetscFunctionReturn(PETSC_SUCCESS);
10187 }
10188 
10189 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
10190 {
10191   PetscSection gsc, gsf;
10192   PetscInt     m, n;
10193   void        *ctx;
10194   DM           cdm;
10195   PetscBool    regular;
10196 
10197   PetscFunctionBegin;
10198   if (dmFine == dmCoarse) {
10199     DM            dmc;
10200     PetscDS       ds;
10201     PetscWeakForm wf;
10202     Vec           u;
10203     IS            cellIS;
10204     PetscFormKey  key;
10205     PetscInt      depth;
10206 
10207     PetscCall(DMClone(dmFine, &dmc));
10208     PetscCall(DMCopyDisc(dmFine, dmc));
10209     PetscCall(DMGetDS(dmc, &ds));
10210     PetscCall(PetscDSGetWeakForm(ds, &wf));
10211     PetscCall(PetscWeakFormClear(wf));
10212     for (PetscInt f = 0; f < dmc->Nf; ++f) PetscCall(PetscDSSetJacobian(ds, f, f, g0_identity_private, NULL, NULL, NULL));
10213     PetscCall(DMCreateMatrix(dmc, mass));
10214     PetscCall(DMGetLocalVector(dmc, &u));
10215     PetscCall(DMPlexGetDepth(dmc, &depth));
10216     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
10217     PetscCall(MatZeroEntries(*mass));
10218     key.label = NULL;
10219     key.value = 0;
10220     key.field = 0;
10221     key.part  = 0;
10222     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
10223     PetscCall(ISDestroy(&cellIS));
10224     PetscCall(DMRestoreLocalVector(dmc, &u));
10225     PetscCall(DMDestroy(&dmc));
10226   } else {
10227     PetscCall(DMGetGlobalSection(dmFine, &gsf));
10228     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10229     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10230     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10231 
10232     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
10233     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10234     PetscCall(MatSetType(*mass, dmCoarse->mattype));
10235     PetscCall(DMGetApplicationContext(dmFine, &ctx));
10236 
10237     PetscCall(DMGetCoarseDM(dmFine, &cdm));
10238     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10239     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
10240     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
10241   }
10242   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
10243   PetscFunctionReturn(PETSC_SUCCESS);
10244 }
10245 
10246 /*@
10247   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10248 
10249   Input Parameter:
10250 . dm - The `DMPLEX` object
10251 
10252   Output Parameter:
10253 . regular - The flag
10254 
10255   Level: intermediate
10256 
10257 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
10258 @*/
10259 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
10260 {
10261   PetscFunctionBegin;
10262   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10263   PetscAssertPointer(regular, 2);
10264   *regular = ((DM_Plex *)dm->data)->regularRefinement;
10265   PetscFunctionReturn(PETSC_SUCCESS);
10266 }
10267 
10268 /*@
10269   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10270 
10271   Input Parameters:
10272 + dm      - The `DMPLEX` object
10273 - regular - The flag
10274 
10275   Level: intermediate
10276 
10277 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
10278 @*/
10279 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
10280 {
10281   PetscFunctionBegin;
10282   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10283   ((DM_Plex *)dm->data)->regularRefinement = regular;
10284   PetscFunctionReturn(PETSC_SUCCESS);
10285 }
10286 
10287 /*@
10288   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
10289   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
10290 
10291   Not Collective
10292 
10293   Input Parameter:
10294 . dm - The `DMPLEX` object
10295 
10296   Output Parameters:
10297 + anchorSection - If not `NULL`, set to the section describing which points anchor the constrained points.
10298 - anchorIS      - If not `NULL`, set to the list of anchors indexed by `anchorSection`
10299 
10300   Level: intermediate
10301 
10302 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
10303 @*/
10304 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
10305 {
10306   DM_Plex *plex = (DM_Plex *)dm->data;
10307 
10308   PetscFunctionBegin;
10309   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10310   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
10311   if (anchorSection) *anchorSection = plex->anchorSection;
10312   if (anchorIS) *anchorIS = plex->anchorIS;
10313   PetscFunctionReturn(PETSC_SUCCESS);
10314 }
10315 
10316 /*@
10317   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.
10318 
10319   Collective
10320 
10321   Input Parameters:
10322 + dm            - The `DMPLEX` object
10323 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
10324                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10325 - anchorIS      - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10326 
10327   Level: intermediate
10328 
10329   Notes:
10330   Unlike boundary conditions, when a point's degrees of freedom in a section are constrained to
10331   an outside value, the anchor constraints set a point's degrees of freedom to be a linear
10332   combination of other points' degrees of freedom.
10333 
10334   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
10335   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
10336 
10337   The reference counts of `anchorSection` and `anchorIS` are incremented.
10338 
10339 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
10340 @*/
10341 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
10342 {
10343   DM_Plex    *plex = (DM_Plex *)dm->data;
10344   PetscMPIInt result;
10345 
10346   PetscFunctionBegin;
10347   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10348   if (anchorSection) {
10349     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
10350     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
10351     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
10352   }
10353   if (anchorIS) {
10354     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
10355     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
10356     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
10357   }
10358 
10359   PetscCall(PetscObjectReference((PetscObject)anchorSection));
10360   PetscCall(PetscSectionDestroy(&plex->anchorSection));
10361   plex->anchorSection = anchorSection;
10362 
10363   PetscCall(PetscObjectReference((PetscObject)anchorIS));
10364   PetscCall(ISDestroy(&plex->anchorIS));
10365   plex->anchorIS = anchorIS;
10366 
10367   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
10368     PetscInt        size, a, pStart, pEnd;
10369     const PetscInt *anchors;
10370 
10371     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10372     PetscCall(ISGetLocalSize(anchorIS, &size));
10373     PetscCall(ISGetIndices(anchorIS, &anchors));
10374     for (a = 0; a < size; a++) {
10375       PetscInt p;
10376 
10377       p = anchors[a];
10378       if (p >= pStart && p < pEnd) {
10379         PetscInt dof;
10380 
10381         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10382         if (dof) {
10383           PetscCall(ISRestoreIndices(anchorIS, &anchors));
10384           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
10385         }
10386       }
10387     }
10388     PetscCall(ISRestoreIndices(anchorIS, &anchors));
10389   }
10390   /* reset the generic constraints */
10391   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
10392   PetscFunctionReturn(PETSC_SUCCESS);
10393 }
10394 
10395 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
10396 {
10397   PetscSection anchorSection;
10398   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
10399 
10400   PetscFunctionBegin;
10401   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10402   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10403   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
10404   PetscCall(PetscSectionGetNumFields(section, &numFields));
10405   if (numFields) {
10406     PetscInt f;
10407     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
10408 
10409     for (f = 0; f < numFields; f++) {
10410       PetscInt numComp;
10411 
10412       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
10413       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
10414     }
10415   }
10416   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10417   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10418   pStart = PetscMax(pStart, sStart);
10419   pEnd   = PetscMin(pEnd, sEnd);
10420   pEnd   = PetscMax(pStart, pEnd);
10421   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
10422   for (p = pStart; p < pEnd; p++) {
10423     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10424     if (dof) {
10425       PetscCall(PetscSectionGetDof(section, p, &dof));
10426       PetscCall(PetscSectionSetDof(*cSec, p, dof));
10427       for (f = 0; f < numFields; f++) {
10428         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
10429         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
10430       }
10431     }
10432   }
10433   PetscCall(PetscSectionSetUp(*cSec));
10434   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
10435   PetscFunctionReturn(PETSC_SUCCESS);
10436 }
10437 
10438 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
10439 {
10440   PetscSection    aSec;
10441   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
10442   const PetscInt *anchors;
10443   PetscInt        numFields, f;
10444   IS              aIS;
10445   MatType         mtype;
10446   PetscBool       iscuda, iskokkos;
10447 
10448   PetscFunctionBegin;
10449   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10450   PetscCall(PetscSectionGetStorageSize(cSec, &m));
10451   PetscCall(PetscSectionGetStorageSize(section, &n));
10452   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
10453   PetscCall(MatSetSizes(*cMat, m, n, m, n));
10454   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
10455   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
10456   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
10457   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
10458   if (iscuda) mtype = MATSEQAIJCUSPARSE;
10459   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
10460   else mtype = MATSEQAIJ;
10461   PetscCall(MatSetType(*cMat, mtype));
10462   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
10463   PetscCall(ISGetIndices(aIS, &anchors));
10464   /* cSec will be a subset of aSec and section */
10465   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
10466   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10467   PetscCall(PetscMalloc1(m + 1, &i));
10468   i[0] = 0;
10469   PetscCall(PetscSectionGetNumFields(section, &numFields));
10470   for (p = pStart; p < pEnd; p++) {
10471     PetscInt rDof, rOff, r;
10472 
10473     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10474     if (!rDof) continue;
10475     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10476     if (numFields) {
10477       for (f = 0; f < numFields; f++) {
10478         annz = 0;
10479         for (r = 0; r < rDof; r++) {
10480           a = anchors[rOff + r];
10481           if (a < sStart || a >= sEnd) continue;
10482           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10483           annz += aDof;
10484         }
10485         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10486         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
10487         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10488       }
10489     } else {
10490       annz = 0;
10491       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10492       for (q = 0; q < dof; q++) {
10493         a = anchors[rOff + q];
10494         if (a < sStart || a >= sEnd) continue;
10495         PetscCall(PetscSectionGetDof(section, a, &aDof));
10496         annz += aDof;
10497       }
10498       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10499       PetscCall(PetscSectionGetOffset(cSec, p, &off));
10500       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10501     }
10502   }
10503   nnz = i[m];
10504   PetscCall(PetscMalloc1(nnz, &j));
10505   offset = 0;
10506   for (p = pStart; p < pEnd; p++) {
10507     if (numFields) {
10508       for (f = 0; f < numFields; f++) {
10509         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10510         for (q = 0; q < dof; q++) {
10511           PetscInt rDof, rOff, r;
10512           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10513           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10514           for (r = 0; r < rDof; r++) {
10515             PetscInt s;
10516 
10517             a = anchors[rOff + r];
10518             if (a < sStart || a >= sEnd) continue;
10519             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10520             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
10521             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10522           }
10523         }
10524       }
10525     } else {
10526       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10527       for (q = 0; q < dof; q++) {
10528         PetscInt rDof, rOff, r;
10529         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10530         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10531         for (r = 0; r < rDof; r++) {
10532           PetscInt s;
10533 
10534           a = anchors[rOff + r];
10535           if (a < sStart || a >= sEnd) continue;
10536           PetscCall(PetscSectionGetDof(section, a, &aDof));
10537           PetscCall(PetscSectionGetOffset(section, a, &aOff));
10538           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10539         }
10540       }
10541     }
10542   }
10543   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
10544   PetscCall(PetscFree(i));
10545   PetscCall(PetscFree(j));
10546   PetscCall(ISRestoreIndices(aIS, &anchors));
10547   PetscFunctionReturn(PETSC_SUCCESS);
10548 }
10549 
10550 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
10551 {
10552   DM_Plex     *plex = (DM_Plex *)dm->data;
10553   PetscSection anchorSection, section, cSec;
10554   Mat          cMat;
10555 
10556   PetscFunctionBegin;
10557   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10558   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10559   if (anchorSection) {
10560     PetscInt Nf;
10561 
10562     PetscCall(DMGetLocalSection(dm, &section));
10563     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
10564     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
10565     PetscCall(DMGetNumFields(dm, &Nf));
10566     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
10567     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
10568     PetscCall(PetscSectionDestroy(&cSec));
10569     PetscCall(MatDestroy(&cMat));
10570   }
10571   PetscFunctionReturn(PETSC_SUCCESS);
10572 }
10573 
10574 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10575 {
10576   IS           subis;
10577   PetscSection section, subsection;
10578 
10579   PetscFunctionBegin;
10580   PetscCall(DMGetLocalSection(dm, &section));
10581   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10582   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10583   /* Create subdomain */
10584   PetscCall(DMPlexFilter(dm, label, value, PETSC_FALSE, PETSC_FALSE, NULL, subdm));
10585   /* Create submodel */
10586   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10587   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10588   PetscCall(DMSetLocalSection(*subdm, subsection));
10589   PetscCall(PetscSectionDestroy(&subsection));
10590   PetscCall(DMCopyDisc(dm, *subdm));
10591   /* Create map from submodel to global model */
10592   if (is) {
10593     PetscSection    sectionGlobal, subsectionGlobal;
10594     IS              spIS;
10595     const PetscInt *spmap;
10596     PetscInt       *subIndices;
10597     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10598     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10599 
10600     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10601     PetscCall(ISGetIndices(spIS, &spmap));
10602     PetscCall(PetscSectionGetNumFields(section, &Nf));
10603     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10604     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10605     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10606     for (p = pStart; p < pEnd; ++p) {
10607       PetscInt gdof, pSubSize = 0;
10608 
10609       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10610       if (gdof > 0) {
10611         for (f = 0; f < Nf; ++f) {
10612           PetscInt fdof, fcdof;
10613 
10614           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10615           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10616           pSubSize += fdof - fcdof;
10617         }
10618         subSize += pSubSize;
10619         if (pSubSize) {
10620           if (bs < 0) {
10621             bs = pSubSize;
10622           } else if (bs != pSubSize) {
10623             /* Layout does not admit a pointwise block size */
10624             bs = 1;
10625           }
10626         }
10627       }
10628     }
10629     /* Must have same blocksize on all procs (some might have no points) */
10630     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
10631     bsLocal[1] = bs;
10632     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10633     if (bsMinMax[0] != bsMinMax[1]) {
10634       bs = 1;
10635     } else {
10636       bs = bsMinMax[0];
10637     }
10638     PetscCall(PetscMalloc1(subSize, &subIndices));
10639     for (p = pStart; p < pEnd; ++p) {
10640       PetscInt gdof, goff;
10641 
10642       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10643       if (gdof > 0) {
10644         const PetscInt point = spmap[p];
10645 
10646         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10647         for (f = 0; f < Nf; ++f) {
10648           PetscInt fdof, fcdof, fc, f2, poff = 0;
10649 
10650           /* Can get rid of this loop by storing field information in the global section */
10651           for (f2 = 0; f2 < f; ++f2) {
10652             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10653             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10654             poff += fdof - fcdof;
10655           }
10656           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10657           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10658           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10659         }
10660       }
10661     }
10662     PetscCall(ISRestoreIndices(spIS, &spmap));
10663     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10664     if (bs > 1) {
10665       /* We need to check that the block size does not come from non-contiguous fields */
10666       PetscInt i, j, set = 1;
10667       for (i = 0; i < subSize; i += bs) {
10668         for (j = 0; j < bs; ++j) {
10669           if (subIndices[i + j] != subIndices[i] + j) {
10670             set = 0;
10671             break;
10672           }
10673         }
10674       }
10675       if (set) PetscCall(ISSetBlockSize(*is, bs));
10676     }
10677     /* Attach nullspace */
10678     for (f = 0; f < Nf; ++f) {
10679       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10680       if ((*subdm)->nullspaceConstructors[f]) break;
10681     }
10682     if (f < Nf) {
10683       MatNullSpace nullSpace;
10684       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10685 
10686       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10687       PetscCall(MatNullSpaceDestroy(&nullSpace));
10688     }
10689   }
10690   PetscFunctionReturn(PETSC_SUCCESS);
10691 }
10692 
10693 /*@
10694   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10695 
10696   Input Parameters:
10697 + dm    - The `DM`
10698 - dummy - unused argument
10699 
10700   Options Database Key:
10701 . -dm_plex_monitor_throughput - Activate the monitor
10702 
10703   Level: developer
10704 
10705 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10706 @*/
10707 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10708 {
10709   PetscLogHandler default_handler;
10710 
10711   PetscFunctionBegin;
10712   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10713   PetscCall(PetscLogGetDefaultHandler(&default_handler));
10714   if (default_handler) {
10715     PetscLogEvent      event;
10716     PetscEventPerfInfo eventInfo;
10717     PetscReal          cellRate, flopRate;
10718     PetscInt           cStart, cEnd, Nf, N;
10719     const char        *name;
10720 
10721     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10722     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10723     PetscCall(DMGetNumFields(dm, &Nf));
10724     PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10725     PetscCall(PetscLogEventGetPerfInfo(PETSC_DEFAULT, event, &eventInfo));
10726     N        = (cEnd - cStart) * Nf * eventInfo.count;
10727     flopRate = eventInfo.flops / eventInfo.time;
10728     cellRate = N / eventInfo.time;
10729     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)));
10730   } else {
10731     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.");
10732   }
10733   PetscFunctionReturn(PETSC_SUCCESS);
10734 }
10735