xref: /petsc/src/dm/impls/plex/plex.c (revision a74aa4897f0ac1dca26412b36bff6c10ad0e8ed3) !
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         PetscScalar       *coords = NULL, *a = NULL;
430         const PetscScalar *coords_arr;
431         PetscBool          isDG;
432         PetscInt           numCoords, color[4] = {-1, -1, -1, -1};
433 
434         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
435         if (a) {
436           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
437           color[1] = color[2] = color[3] = color[0];
438         } else {
439           PetscScalar *vals = NULL;
440           PetscInt     numVals, va;
441 
442           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
443           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);
444           switch (numVals / Nc) {
445           case 3: /* P1 Triangle */
446           case 4: /* P1 Quadrangle */
447             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
448             break;
449           case 6: /* P2 Triangle */
450           case 8: /* P2 Quadrangle */
451             for (va = 0; va < numVals / (Nc * 2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp + numVals / (Nc * 2)]), vbound[0], vbound[1]);
452             break;
453           default:
454             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals / Nc);
455           }
456           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
457         }
458         PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
459         switch (numCoords) {
460         case 6:
461         case 12: /* Localized triangle */
462           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]));
463           break;
464         case 8:
465         case 16: /* Localized quadrilateral */
466           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]));
467           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]));
468           break;
469         default:
470           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
471         }
472         PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
473       }
474       PetscCall(VecRestoreArrayRead(fv, &array));
475       PetscCall(PetscDrawFlush(draw));
476       PetscCall(PetscDrawPause(draw));
477       PetscCall(PetscDrawSave(draw));
478     }
479     if (Nf > 1) {
480       PetscCall(VecRestoreSubVector(v, fis, &fv));
481       PetscCall(ISDestroy(&fis));
482       PetscCall(DMDestroy(&fdm));
483     }
484   }
485   PetscFunctionReturn(PETSC_SUCCESS);
486 }
487 
488 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
489 {
490   DM        dm;
491   PetscDraw draw;
492   PetscInt  dim;
493   PetscBool isnull;
494 
495   PetscFunctionBegin;
496   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
497   PetscCall(PetscDrawIsNull(draw, &isnull));
498   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
499 
500   PetscCall(VecGetDM(v, &dm));
501   PetscCall(DMGetCoordinateDim(dm, &dim));
502   switch (dim) {
503   case 1:
504     PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));
505     break;
506   case 2:
507     PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));
508     break;
509   default:
510     SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
511   }
512   PetscFunctionReturn(PETSC_SUCCESS);
513 }
514 
515 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
516 {
517   DM                      dm;
518   Vec                     locv;
519   const char             *name;
520   PetscSection            section;
521   PetscInt                pStart, pEnd;
522   PetscInt                numFields;
523   PetscViewerVTKFieldType ft;
524 
525   PetscFunctionBegin;
526   PetscCall(VecGetDM(v, &dm));
527   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
528   PetscCall(PetscObjectGetName((PetscObject)v, &name));
529   PetscCall(PetscObjectSetName((PetscObject)locv, name));
530   PetscCall(VecCopy(v, locv));
531   PetscCall(DMGetLocalSection(dm, &section));
532   PetscCall(PetscSectionGetNumFields(section, &numFields));
533   if (!numFields) {
534     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
535     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE, (PetscObject)locv));
536   } else {
537     PetscInt f;
538 
539     for (f = 0; f < numFields; f++) {
540       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
541       if (ft == PETSC_VTK_INVALID) continue;
542       PetscCall(PetscObjectReference((PetscObject)locv));
543       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE, (PetscObject)locv));
544     }
545     PetscCall(VecDestroy(&locv));
546   }
547   PetscFunctionReturn(PETSC_SUCCESS);
548 }
549 
550 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
551 {
552   DM        dm;
553   PetscBool isvtk, ishdf5, isdraw, isglvis, iscgns;
554 
555   PetscFunctionBegin;
556   PetscCall(VecGetDM(v, &dm));
557   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
558   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
559   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
560   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
561   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
562   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
563   if (isvtk || ishdf5 || isdraw || isglvis || iscgns) {
564     PetscInt    i, numFields;
565     PetscObject fe;
566     PetscBool   fem  = PETSC_FALSE;
567     Vec         locv = v;
568     const char *name;
569     PetscInt    step;
570     PetscReal   time;
571 
572     PetscCall(DMGetNumFields(dm, &numFields));
573     for (i = 0; i < numFields; i++) {
574       PetscCall(DMGetField(dm, i, NULL, &fe));
575       if (fe->classid == PETSCFE_CLASSID) {
576         fem = PETSC_TRUE;
577         break;
578       }
579     }
580     if (fem) {
581       PetscObject isZero;
582 
583       PetscCall(DMGetLocalVector(dm, &locv));
584       PetscCall(PetscObjectGetName((PetscObject)v, &name));
585       PetscCall(PetscObjectSetName((PetscObject)locv, name));
586       PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
587       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
588       PetscCall(VecCopy(v, locv));
589       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
590       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
591     }
592     if (isvtk) {
593       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
594     } else if (ishdf5) {
595 #if defined(PETSC_HAVE_HDF5)
596       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
597 #else
598       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
599 #endif
600     } else if (isdraw) {
601       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
602     } else if (isglvis) {
603       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
604       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
605       PetscCall(VecView_GLVis(locv, viewer));
606     } else if (iscgns) {
607 #if defined(PETSC_HAVE_CGNS)
608       PetscCall(VecView_Plex_Local_CGNS(locv, viewer));
609 #else
610       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
611 #endif
612     }
613     if (fem) {
614       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
615       PetscCall(DMRestoreLocalVector(dm, &locv));
616     }
617   } else {
618     PetscBool isseq;
619 
620     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
621     if (isseq) PetscCall(VecView_Seq(v, viewer));
622     else PetscCall(VecView_MPI(v, viewer));
623   }
624   PetscFunctionReturn(PETSC_SUCCESS);
625 }
626 
627 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
628 {
629   DM        dm;
630   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii, iscgns;
631 
632   PetscFunctionBegin;
633   PetscCall(VecGetDM(v, &dm));
634   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
635   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
636   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
637   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
638   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
639   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
640   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
641   if (isvtk || isdraw || isglvis || iscgns) {
642     Vec         locv;
643     PetscObject isZero;
644     const char *name;
645 
646     PetscCall(DMGetLocalVector(dm, &locv));
647     PetscCall(PetscObjectGetName((PetscObject)v, &name));
648     PetscCall(PetscObjectSetName((PetscObject)locv, name));
649     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
650     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
651     PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
652     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
653     PetscCall(VecView_Plex_Local(locv, viewer));
654     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
655     PetscCall(DMRestoreLocalVector(dm, &locv));
656   } else if (ishdf5) {
657 #if defined(PETSC_HAVE_HDF5)
658     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
659 #else
660     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
661 #endif
662   } else if (isexodusii) {
663 #if defined(PETSC_HAVE_EXODUSII)
664     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
665 #else
666     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
667 #endif
668   } else {
669     PetscBool isseq;
670 
671     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
672     if (isseq) PetscCall(VecView_Seq(v, viewer));
673     else PetscCall(VecView_MPI(v, viewer));
674   }
675   PetscFunctionReturn(PETSC_SUCCESS);
676 }
677 
678 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
679 {
680   DM                dm;
681   MPI_Comm          comm;
682   PetscViewerFormat format;
683   Vec               v;
684   PetscBool         isvtk, ishdf5;
685 
686   PetscFunctionBegin;
687   PetscCall(VecGetDM(originalv, &dm));
688   PetscCall(PetscObjectGetComm((PetscObject)originalv, &comm));
689   PetscCheck(dm, comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
690   PetscCall(PetscViewerGetFormat(viewer, &format));
691   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
692   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
693   if (format == PETSC_VIEWER_NATIVE) {
694     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
695     /* this need a better fix */
696     if (dm->useNatural) {
697       if (dm->sfNatural) {
698         const char *vecname;
699         PetscInt    n, nroots;
700 
701         PetscCall(VecGetLocalSize(originalv, &n));
702         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
703         if (n == nroots) {
704           PetscCall(DMPlexCreateNaturalVector(dm, &v));
705           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
706           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
707           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
708           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
709         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
710       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
711     } else v = originalv;
712   } else v = originalv;
713 
714   if (ishdf5) {
715 #if defined(PETSC_HAVE_HDF5)
716     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
717 #else
718     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
719 #endif
720   } else if (isvtk) {
721     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
722   } else {
723     PetscBool isseq;
724 
725     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
726     if (isseq) PetscCall(VecView_Seq(v, viewer));
727     else PetscCall(VecView_MPI(v, viewer));
728   }
729   if (v != originalv) PetscCall(VecDestroy(&v));
730   PetscFunctionReturn(PETSC_SUCCESS);
731 }
732 
733 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
734 {
735   DM        dm;
736   PetscBool ishdf5;
737 
738   PetscFunctionBegin;
739   PetscCall(VecGetDM(v, &dm));
740   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
741   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
742   if (ishdf5) {
743     DM          dmBC;
744     Vec         gv;
745     const char *name;
746 
747     PetscCall(DMGetOutputDM(dm, &dmBC));
748     PetscCall(DMGetGlobalVector(dmBC, &gv));
749     PetscCall(PetscObjectGetName((PetscObject)v, &name));
750     PetscCall(PetscObjectSetName((PetscObject)gv, name));
751     PetscCall(VecLoad_Default(gv, viewer));
752     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
753     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
754     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
755   } else PetscCall(VecLoad_Default(v, viewer));
756   PetscFunctionReturn(PETSC_SUCCESS);
757 }
758 
759 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
760 {
761   DM        dm;
762   PetscBool ishdf5, isexodusii;
763 
764   PetscFunctionBegin;
765   PetscCall(VecGetDM(v, &dm));
766   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
767   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
768   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
769   if (ishdf5) {
770 #if defined(PETSC_HAVE_HDF5)
771     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
772 #else
773     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
774 #endif
775   } else if (isexodusii) {
776 #if defined(PETSC_HAVE_EXODUSII)
777     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
778 #else
779     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
780 #endif
781   } else PetscCall(VecLoad_Default(v, viewer));
782   PetscFunctionReturn(PETSC_SUCCESS);
783 }
784 
785 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
786 {
787   DM                dm;
788   PetscViewerFormat format;
789   PetscBool         ishdf5;
790 
791   PetscFunctionBegin;
792   PetscCall(VecGetDM(originalv, &dm));
793   PetscCheck(dm, PetscObjectComm((PetscObject)originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
794   PetscCall(PetscViewerGetFormat(viewer, &format));
795   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
796   if (format == PETSC_VIEWER_NATIVE) {
797     if (dm->useNatural) {
798       if (dm->sfNatural) {
799         if (ishdf5) {
800 #if defined(PETSC_HAVE_HDF5)
801           Vec         v;
802           const char *vecname;
803 
804           PetscCall(DMPlexCreateNaturalVector(dm, &v));
805           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
806           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
807           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
808           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
809           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
810           PetscCall(VecDestroy(&v));
811 #else
812           SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
813 #endif
814         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
815       }
816     } else PetscCall(VecLoad_Default(originalv, viewer));
817   }
818   PetscFunctionReturn(PETSC_SUCCESS);
819 }
820 
821 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
822 {
823   PetscSection       coordSection;
824   Vec                coordinates;
825   DMLabel            depthLabel, celltypeLabel;
826   const char        *name[4];
827   const PetscScalar *a;
828   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
829 
830   PetscFunctionBegin;
831   PetscCall(DMGetDimension(dm, &dim));
832   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
833   PetscCall(DMGetCoordinateSection(dm, &coordSection));
834   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
835   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
836   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
837   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
838   PetscCall(VecGetArrayRead(coordinates, &a));
839   name[0]       = "vertex";
840   name[1]       = "edge";
841   name[dim - 1] = "face";
842   name[dim]     = "cell";
843   for (c = cStart; c < cEnd; ++c) {
844     PetscInt *closure = NULL;
845     PetscInt  closureSize, cl, ct;
846 
847     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
848     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
849     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
850     PetscCall(PetscViewerASCIIPushTab(viewer));
851     for (cl = 0; cl < closureSize * 2; cl += 2) {
852       PetscInt point = closure[cl], depth, dof, off, d, p;
853 
854       if ((point < pStart) || (point >= pEnd)) continue;
855       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
856       if (!dof) continue;
857       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
858       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
859       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
860       for (p = 0; p < dof / dim; ++p) {
861         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
862         for (d = 0; d < dim; ++d) {
863           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
864           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double)PetscRealPart(a[off + p * dim + d])));
865         }
866         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
867       }
868       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
869     }
870     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
871     PetscCall(PetscViewerASCIIPopTab(viewer));
872   }
873   PetscCall(VecRestoreArrayRead(coordinates, &a));
874   PetscFunctionReturn(PETSC_SUCCESS);
875 }
876 
877 typedef enum {
878   CS_CARTESIAN,
879   CS_POLAR,
880   CS_CYLINDRICAL,
881   CS_SPHERICAL
882 } CoordSystem;
883 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
884 
885 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
886 {
887   PetscInt i;
888 
889   PetscFunctionBegin;
890   if (dim > 3) {
891     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)PetscRealPart(x[i])));
892   } else {
893     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
894 
895     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
896     switch (cs) {
897     case CS_CARTESIAN:
898       for (i = 0; i < dim; ++i) trcoords[i] = coords[i];
899       break;
900     case CS_POLAR:
901       PetscCheck(dim == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
902       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
903       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
904       break;
905     case CS_CYLINDRICAL:
906       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
907       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
908       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
909       trcoords[2] = coords[2];
910       break;
911     case CS_SPHERICAL:
912       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
913       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
914       trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
915       trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
916       break;
917     }
918     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)trcoords[i]));
919   }
920   PetscFunctionReturn(PETSC_SUCCESS);
921 }
922 
923 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
924 {
925   DM_Plex          *mesh = (DM_Plex *)dm->data;
926   DM                cdm, cdmCell;
927   PetscSection      coordSection, coordSectionCell;
928   Vec               coordinates, coordinatesCell;
929   PetscViewerFormat format;
930 
931   PetscFunctionBegin;
932   PetscCall(PetscViewerGetFormat(viewer, &format));
933   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
934     const char *name;
935     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
936     PetscInt    pStart, pEnd, p, numLabels, l;
937     PetscMPIInt rank, size;
938 
939     PetscCall(DMGetCoordinateDM(dm, &cdm));
940     PetscCall(DMGetCoordinateSection(dm, &coordSection));
941     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
942     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
943     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
944     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
945     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
946     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
947     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
948     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
949     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
950     PetscCall(DMGetDimension(dm, &dim));
951     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
952     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
953     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
954     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
955     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
956     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
957     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
958     for (p = pStart; p < pEnd; ++p) {
959       PetscInt dof, off, s;
960 
961       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
962       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
963       for (s = off; s < off + dof; ++s) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
964     }
965     PetscCall(PetscViewerFlush(viewer));
966     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
967     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
968     for (p = pStart; p < pEnd; ++p) {
969       PetscInt dof, off, c;
970 
971       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
972       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
973       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]));
974     }
975     PetscCall(PetscViewerFlush(viewer));
976     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
977     if (coordSection && coordinates) {
978       CoordSystem        cs = CS_CARTESIAN;
979       const PetscScalar *array, *arrayCell = NULL;
980       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_MAX_INT, pcEnd = PETSC_MIN_INT, pStart, pEnd, p;
981       PetscMPIInt        rank;
982       const char        *name;
983 
984       PetscCall(PetscOptionsGetEnum(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *)&cs, NULL));
985       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
986       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
987       PetscCheck(Nf == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
988       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
989       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
990       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
991       pStart = PetscMin(pvStart, pcStart);
992       pEnd   = PetscMax(pvEnd, pcEnd);
993       PetscCall(PetscObjectGetName((PetscObject)coordinates, &name));
994       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
995       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
996       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
997 
998       PetscCall(VecGetArrayRead(coordinates, &array));
999       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
1000       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1001       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
1002       for (p = pStart; p < pEnd; ++p) {
1003         PetscInt dof, off;
1004 
1005         if (p >= pvStart && p < pvEnd) {
1006           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
1007           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
1008           if (dof) {
1009             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1010             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
1011             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1012           }
1013         }
1014         if (cdmCell && p >= pcStart && p < pcEnd) {
1015           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
1016           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
1017           if (dof) {
1018             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1019             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
1020             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1021           }
1022         }
1023       }
1024       PetscCall(PetscViewerFlush(viewer));
1025       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1026       PetscCall(VecRestoreArrayRead(coordinates, &array));
1027       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
1028     }
1029     PetscCall(DMGetNumLabels(dm, &numLabels));
1030     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1031     for (l = 0; l < numLabels; ++l) {
1032       DMLabel     label;
1033       PetscBool   isdepth;
1034       const char *name;
1035 
1036       PetscCall(DMGetLabelName(dm, l, &name));
1037       PetscCall(PetscStrcmp(name, "depth", &isdepth));
1038       if (isdepth) continue;
1039       PetscCall(DMGetLabel(dm, name, &label));
1040       PetscCall(DMLabelView(label, viewer));
1041     }
1042     if (size > 1) {
1043       PetscSF sf;
1044 
1045       PetscCall(DMGetPointSF(dm, &sf));
1046       PetscCall(PetscSFView(sf, viewer));
1047     }
1048     if (mesh->periodic.face_sfs)
1049       for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(PetscSFView(mesh->periodic.face_sfs[i], viewer));
1050     PetscCall(PetscViewerFlush(viewer));
1051   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
1052     const char  *name, *color;
1053     const char  *defcolors[3]  = {"gray", "orange", "green"};
1054     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
1055     char         lname[PETSC_MAX_PATH_LEN];
1056     PetscReal    scale      = 2.0;
1057     PetscReal    tikzscale  = 1.0;
1058     PetscBool    useNumbers = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
1059     double       tcoords[3];
1060     PetscScalar *coords;
1061     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;
1062     PetscMPIInt  rank, size;
1063     char       **names, **colors, **lcolors;
1064     PetscBool    flg, lflg;
1065     PetscBT      wp = NULL;
1066     PetscInt     pEnd, pStart;
1067 
1068     PetscCall(DMGetCoordinateDM(dm, &cdm));
1069     PetscCall(DMGetCoordinateSection(dm, &coordSection));
1070     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1071     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
1072     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
1073     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
1074     PetscCall(DMGetDimension(dm, &dim));
1075     PetscCall(DMPlexGetDepth(dm, &depth));
1076     PetscCall(DMGetNumLabels(dm, &numLabels));
1077     numLabels  = PetscMax(numLabels, 10);
1078     numColors  = 10;
1079     numLColors = 10;
1080     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
1081     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
1082     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
1083     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
1084     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
1085     for (d = 0; d < 4; ++d) drawColors[d] = PETSC_TRUE;
1086     n = 4;
1087     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
1088     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1089     n = 4;
1090     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
1091     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1092     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
1093     if (!useLabels) numLabels = 0;
1094     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
1095     if (!useColors) {
1096       numColors = 3;
1097       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
1098     }
1099     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
1100     if (!useColors) {
1101       numLColors = 4;
1102       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
1103     }
1104     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
1105     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
1106     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
1107     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
1108     if (depth < dim) plotEdges = PETSC_FALSE;
1109     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
1110 
1111     /* filter points with labelvalue != labeldefaultvalue */
1112     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1113     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1114     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
1115     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1116     PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
1117     if (lflg) {
1118       DMLabel lbl;
1119 
1120       PetscCall(DMGetLabel(dm, lname, &lbl));
1121       if (lbl) {
1122         PetscInt val, defval;
1123 
1124         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
1125         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
1126         for (c = pStart; c < pEnd; c++) {
1127           PetscInt *closure = NULL;
1128           PetscInt  closureSize;
1129 
1130           PetscCall(DMLabelGetValue(lbl, c, &val));
1131           if (val == defval) continue;
1132 
1133           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1134           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
1135           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1136         }
1137       }
1138     }
1139 
1140     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1141     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1142     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1143     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1144 \\documentclass[tikz]{standalone}\n\n\
1145 \\usepackage{pgflibraryshapes}\n\
1146 \\usetikzlibrary{backgrounds}\n\
1147 \\usetikzlibrary{arrows}\n\
1148 \\begin{document}\n"));
1149     if (size > 1) {
1150       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1151       for (p = 0; p < size; ++p) {
1152         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1153         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1154       }
1155       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1156     }
1157     if (drawHasse) {
1158       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, PetscMax(fEnd - fStart, cEnd - cStart)));
1159 
1160       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1161       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1162       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1163       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1164       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1165       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1166       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1167       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1168       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fStart}{%" PetscInt_FMT "}\n", fStart));
1169       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fEnd}{%" PetscInt_FMT "}\n", fEnd - 1));
1170       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fShift}{%.2f}\n", 3 + (maxStratum - (fEnd - fStart)) / 2.));
1171       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numFaces}{%" PetscInt_FMT "}\n", fEnd - fStart));
1172       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1173       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1174       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1175       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1176     }
1177     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1178 
1179     /* Plot vertices */
1180     PetscCall(VecGetArray(coordinates, &coords));
1181     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1182     for (v = vStart; v < vEnd; ++v) {
1183       PetscInt  off, dof, d;
1184       PetscBool isLabeled = PETSC_FALSE;
1185 
1186       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1187       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1188       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1189       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1190       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1191       for (d = 0; d < dof; ++d) {
1192         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1193         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1194       }
1195       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1196       if (dim == 3) {
1197         PetscReal tmp = tcoords[1];
1198         tcoords[1]    = tcoords[2];
1199         tcoords[2]    = -tmp;
1200       }
1201       for (d = 0; d < dof; ++d) {
1202         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1203         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1204       }
1205       if (drawHasse) color = colors[0 % numColors];
1206       else color = colors[rank % numColors];
1207       for (l = 0; l < numLabels; ++l) {
1208         PetscInt val;
1209         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1210         if (val >= 0) {
1211           color     = lcolors[l % numLColors];
1212           isLabeled = PETSC_TRUE;
1213           break;
1214         }
1215       }
1216       if (drawNumbers[0]) {
1217         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1218       } else if (drawColors[0]) {
1219         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1220       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1221     }
1222     PetscCall(VecRestoreArray(coordinates, &coords));
1223     PetscCall(PetscViewerFlush(viewer));
1224     /* Plot edges */
1225     if (plotEdges) {
1226       PetscCall(VecGetArray(coordinates, &coords));
1227       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1228       for (e = eStart; e < eEnd; ++e) {
1229         const PetscInt *cone;
1230         PetscInt        coneSize, offA, offB, dof, d;
1231 
1232         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1233         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1234         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1235         PetscCall(DMPlexGetCone(dm, e, &cone));
1236         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1237         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1238         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1239         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1240         for (d = 0; d < dof; ++d) {
1241           tcoords[d] = (double)(0.5 * scale * PetscRealPart(coords[offA + d] + coords[offB + d]));
1242           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1243         }
1244         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1245         if (dim == 3) {
1246           PetscReal tmp = tcoords[1];
1247           tcoords[1]    = tcoords[2];
1248           tcoords[2]    = -tmp;
1249         }
1250         for (d = 0; d < dof; ++d) {
1251           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1252           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1253         }
1254         if (drawHasse) color = colors[1 % numColors];
1255         else color = colors[rank % numColors];
1256         for (l = 0; l < numLabels; ++l) {
1257           PetscInt val;
1258           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1259           if (val >= 0) {
1260             color = lcolors[l % numLColors];
1261             break;
1262           }
1263         }
1264         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1265       }
1266       PetscCall(VecRestoreArray(coordinates, &coords));
1267       PetscCall(PetscViewerFlush(viewer));
1268       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1269     }
1270     /* Plot cells */
1271     if (dim == 3 || !drawNumbers[1]) {
1272       for (e = eStart; e < eEnd; ++e) {
1273         const PetscInt *cone;
1274 
1275         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1276         color = colors[rank % numColors];
1277         for (l = 0; l < numLabels; ++l) {
1278           PetscInt val;
1279           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1280           if (val >= 0) {
1281             color = lcolors[l % numLColors];
1282             break;
1283           }
1284         }
1285         PetscCall(DMPlexGetCone(dm, e, &cone));
1286         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1287       }
1288     } else {
1289       DMPolytopeType ct;
1290 
1291       /* Drawing a 2D polygon */
1292       for (c = cStart; c < cEnd; ++c) {
1293         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1294         PetscCall(DMPlexGetCellType(dm, c, &ct));
1295         if (DMPolytopeTypeIsHybrid(ct)) {
1296           const PetscInt *cone;
1297           PetscInt        coneSize, e;
1298 
1299           PetscCall(DMPlexGetCone(dm, c, &cone));
1300           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1301           for (e = 0; e < coneSize; ++e) {
1302             const PetscInt *econe;
1303 
1304             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1305             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));
1306           }
1307         } else {
1308           PetscInt *closure = NULL;
1309           PetscInt  closureSize, Nv = 0, v;
1310 
1311           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1312           for (p = 0; p < closureSize * 2; p += 2) {
1313             const PetscInt point = closure[p];
1314 
1315             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1316           }
1317           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1318           for (v = 0; v <= Nv; ++v) {
1319             const PetscInt vertex = closure[v % Nv];
1320 
1321             if (v > 0) {
1322               if (plotEdges) {
1323                 const PetscInt *edge;
1324                 PetscInt        endpoints[2], ne;
1325 
1326                 endpoints[0] = closure[v - 1];
1327                 endpoints[1] = vertex;
1328                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1329                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1330                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1331                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1332               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1333             }
1334             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1335           }
1336           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1337           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1338         }
1339       }
1340     }
1341     for (c = cStart; c < cEnd; ++c) {
1342       double             ccoords[3] = {0.0, 0.0, 0.0};
1343       PetscBool          isLabeled  = PETSC_FALSE;
1344       PetscScalar       *cellCoords = NULL;
1345       const PetscScalar *array;
1346       PetscInt           numCoords, cdim, d;
1347       PetscBool          isDG;
1348 
1349       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1350       PetscCall(DMGetCoordinateDim(dm, &cdim));
1351       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1352       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1353       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1354       for (p = 0; p < numCoords / cdim; ++p) {
1355         for (d = 0; d < cdim; ++d) {
1356           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1357           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1358         }
1359         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1360         if (cdim == 3) {
1361           PetscReal tmp = tcoords[1];
1362           tcoords[1]    = tcoords[2];
1363           tcoords[2]    = -tmp;
1364         }
1365         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1366       }
1367       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1368       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1369       for (d = 0; d < cdim; ++d) {
1370         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1371         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)ccoords[d]));
1372       }
1373       if (drawHasse) color = colors[depth % numColors];
1374       else color = colors[rank % numColors];
1375       for (l = 0; l < numLabels; ++l) {
1376         PetscInt val;
1377         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1378         if (val >= 0) {
1379           color     = lcolors[l % numLColors];
1380           isLabeled = PETSC_TRUE;
1381           break;
1382         }
1383       }
1384       if (drawNumbers[dim]) {
1385         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1386       } else if (drawColors[dim]) {
1387         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1388       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1389     }
1390     if (drawHasse) {
1391       int height = 0;
1392 
1393       color = colors[depth % numColors];
1394       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1395       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1396       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1397       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,%d) {\\c};\n", rank, color, height++));
1398       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1399 
1400       if (depth > 2) {
1401         color = colors[1 % numColors];
1402         PetscCall(PetscViewerASCIIPrintf(viewer, "%% Faces\n"));
1403         PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\f in {\\fStart,...,\\fEnd}\n"));
1404         PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1405         PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\f_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\fShift+\\f-\\fStart,%d) {\\f};\n", rank, color, height++));
1406         PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1407       }
1408 
1409       color = colors[1 % numColors];
1410       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1411       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1412       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1413       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,%d) {\\e};\n", rank, color, height++));
1414       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1415 
1416       color = colors[0 % numColors];
1417       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1418       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1419       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1420       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,%d) {\\v};\n", rank, color, height++));
1421       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1422 
1423       for (p = pStart; p < pEnd; ++p) {
1424         const PetscInt *cone;
1425         PetscInt        coneSize, cp;
1426 
1427         PetscCall(DMPlexGetCone(dm, p, &cone));
1428         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1429         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1430       }
1431     }
1432     PetscCall(PetscViewerFlush(viewer));
1433     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1434     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1435     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1436     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1437     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1438     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1439     PetscCall(PetscFree3(names, colors, lcolors));
1440     PetscCall(PetscBTDestroy(&wp));
1441   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1442     Vec                    cown, acown;
1443     VecScatter             sct;
1444     ISLocalToGlobalMapping g2l;
1445     IS                     gid, acis;
1446     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1447     MPI_Group              ggroup, ngroup;
1448     PetscScalar           *array, nid;
1449     const PetscInt        *idxs;
1450     PetscInt              *idxs2, *start, *adjacency, *work;
1451     PetscInt64             lm[3], gm[3];
1452     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1453     PetscMPIInt            d1, d2, rank;
1454 
1455     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1456     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1457 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1458     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1459 #endif
1460     if (ncomm != MPI_COMM_NULL) {
1461       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1462       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1463       d1 = 0;
1464       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1465       nid = d2;
1466       PetscCallMPI(MPI_Group_free(&ggroup));
1467       PetscCallMPI(MPI_Group_free(&ngroup));
1468       PetscCallMPI(MPI_Comm_free(&ncomm));
1469     } else nid = 0.0;
1470 
1471     /* Get connectivity */
1472     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1473     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1474 
1475     /* filter overlapped local cells */
1476     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1477     PetscCall(ISGetIndices(gid, &idxs));
1478     PetscCall(ISGetLocalSize(gid, &cum));
1479     PetscCall(PetscMalloc1(cum, &idxs2));
1480     for (c = cStart, cum = 0; c < cEnd; c++) {
1481       if (idxs[c - cStart] < 0) continue;
1482       idxs2[cum++] = idxs[c - cStart];
1483     }
1484     PetscCall(ISRestoreIndices(gid, &idxs));
1485     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1486     PetscCall(ISDestroy(&gid));
1487     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1488 
1489     /* support for node-aware cell locality */
1490     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1491     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1492     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1493     PetscCall(VecGetArray(cown, &array));
1494     for (c = 0; c < numVertices; c++) array[c] = nid;
1495     PetscCall(VecRestoreArray(cown, &array));
1496     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1497     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1498     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1499     PetscCall(ISDestroy(&acis));
1500     PetscCall(VecScatterDestroy(&sct));
1501     PetscCall(VecDestroy(&cown));
1502 
1503     /* compute edgeCut */
1504     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1505     PetscCall(PetscMalloc1(cum, &work));
1506     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1507     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1508     PetscCall(ISDestroy(&gid));
1509     PetscCall(VecGetArray(acown, &array));
1510     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1511       PetscInt totl;
1512 
1513       totl = start[c + 1] - start[c];
1514       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1515       for (i = 0; i < totl; i++) {
1516         if (work[i] < 0) {
1517           ect += 1;
1518           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1519         }
1520       }
1521     }
1522     PetscCall(PetscFree(work));
1523     PetscCall(VecRestoreArray(acown, &array));
1524     lm[0] = numVertices > 0 ? numVertices : PETSC_MAX_INT;
1525     lm[1] = -numVertices;
1526     PetscCall(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1527     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT, -((double)gm[1]) / ((double)gm[0]), -(PetscInt)gm[1], (PetscInt)gm[0]));
1528     lm[0] = ect;                     /* edgeCut */
1529     lm[1] = ectn;                    /* node-aware edgeCut */
1530     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1531     PetscCall(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1532     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt_FMT ")\n", (PetscInt)gm[2]));
1533 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1534     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), gm[0] ? ((double)gm[1]) / ((double)gm[0]) : 1.));
1535 #else
1536     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), 0.0));
1537 #endif
1538     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1539     PetscCall(PetscFree(start));
1540     PetscCall(PetscFree(adjacency));
1541     PetscCall(VecDestroy(&acown));
1542   } else {
1543     const char    *name;
1544     PetscInt      *sizes, *hybsizes, *ghostsizes;
1545     PetscInt       locDepth, depth, cellHeight, dim, d;
1546     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1547     PetscInt       numLabels, l, maxSize = 17;
1548     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1549     MPI_Comm       comm;
1550     PetscMPIInt    size, rank;
1551 
1552     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1553     PetscCallMPI(MPI_Comm_size(comm, &size));
1554     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1555     PetscCall(DMGetDimension(dm, &dim));
1556     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1557     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1558     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1559     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1560     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1561     PetscCall(DMPlexGetDepth(dm, &locDepth));
1562     PetscCall(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1563     PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &gcStart, &gcEnd));
1564     gcNum = gcEnd - gcStart;
1565     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1566     else PetscCall(PetscCalloc3(3, &sizes, 3, &hybsizes, 3, &ghostsizes));
1567     for (d = 0; d <= depth; d++) {
1568       PetscInt Nc[2] = {0, 0}, ict;
1569 
1570       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1571       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1572       ict = ct0;
1573       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1574       ct0 = (DMPolytopeType)ict;
1575       for (p = pStart; p < pEnd; ++p) {
1576         DMPolytopeType ct;
1577 
1578         PetscCall(DMPlexGetCellType(dm, p, &ct));
1579         if (ct == ct0) ++Nc[0];
1580         else ++Nc[1];
1581       }
1582       if (size < maxSize) {
1583         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes, 1, MPIU_INT, 0, comm));
1584         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1585         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1586         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1587         for (p = 0; p < size; ++p) {
1588           if (rank == 0) {
1589             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p] + hybsizes[p]));
1590             if (hybsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1591             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1592           }
1593         }
1594       } else {
1595         PetscInt locMinMax[2];
1596 
1597         locMinMax[0] = Nc[0] + Nc[1];
1598         locMinMax[1] = Nc[0] + Nc[1];
1599         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1600         locMinMax[0] = Nc[1];
1601         locMinMax[1] = Nc[1];
1602         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1603         if (d == depth) {
1604           locMinMax[0] = gcNum;
1605           locMinMax[1] = gcNum;
1606           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1607         }
1608         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1609         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1610         if (hybsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1611         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1612       }
1613       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1614     }
1615     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1616     {
1617       const PetscReal *maxCell;
1618       const PetscReal *L;
1619       PetscBool        localized;
1620 
1621       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1622       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1623       if (L || localized) {
1624         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1625         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1626         if (L) {
1627           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1628           for (d = 0; d < dim; ++d) {
1629             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1630             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1631           }
1632           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1633         }
1634         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1635         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1636       }
1637     }
1638     PetscCall(DMGetNumLabels(dm, &numLabels));
1639     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1640     for (l = 0; l < numLabels; ++l) {
1641       DMLabel     label;
1642       const char *name;
1643       PetscInt   *values;
1644       PetscInt    numValues, v;
1645 
1646       PetscCall(DMGetLabelName(dm, l, &name));
1647       PetscCall(DMGetLabel(dm, name, &label));
1648       PetscCall(DMLabelGetNumValues(label, &numValues));
1649       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1650 
1651       { // Extract array of DMLabel values so it can be sorted
1652         IS              is_values;
1653         const PetscInt *is_values_local = NULL;
1654 
1655         PetscCall(DMLabelGetValueIS(label, &is_values));
1656         PetscCall(ISGetIndices(is_values, &is_values_local));
1657         PetscCall(PetscMalloc1(numValues, &values));
1658         PetscCall(PetscArraycpy(values, is_values_local, numValues));
1659         PetscCall(PetscSortInt(numValues, values));
1660         PetscCall(ISRestoreIndices(is_values, &is_values_local));
1661         PetscCall(ISDestroy(&is_values));
1662       }
1663       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1664       for (v = 0; v < numValues; ++v) {
1665         PetscInt size;
1666 
1667         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1668         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1669         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1670       }
1671       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1672       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1673       PetscCall(PetscFree(values));
1674     }
1675     {
1676       char    **labelNames;
1677       PetscInt  Nl = numLabels;
1678       PetscBool flg;
1679 
1680       PetscCall(PetscMalloc1(Nl, &labelNames));
1681       PetscCall(PetscOptionsGetStringArray(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1682       for (l = 0; l < Nl; ++l) {
1683         DMLabel label;
1684 
1685         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1686         if (flg) {
1687           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1688           PetscCall(DMLabelView(label, viewer));
1689         }
1690         PetscCall(PetscFree(labelNames[l]));
1691       }
1692       PetscCall(PetscFree(labelNames));
1693     }
1694     /* If no fields are specified, people do not want to see adjacency */
1695     if (dm->Nf) {
1696       PetscInt f;
1697 
1698       for (f = 0; f < dm->Nf; ++f) {
1699         const char *name;
1700 
1701         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1702         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1703         PetscCall(PetscViewerASCIIPushTab(viewer));
1704         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1705         if (dm->fields[f].adjacency[0]) {
1706           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1707           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1708         } else {
1709           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1710           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1711         }
1712         PetscCall(PetscViewerASCIIPopTab(viewer));
1713       }
1714     }
1715     PetscCall(DMGetCoarseDM(dm, &cdm));
1716     if (cdm) {
1717       PetscCall(PetscViewerASCIIPushTab(viewer));
1718       PetscCall(PetscViewerASCIIPrintf(viewer, "Defined by transform from:\n"));
1719       PetscCall(DMPlexView_Ascii(cdm, viewer));
1720       PetscCall(PetscViewerASCIIPopTab(viewer));
1721     }
1722   }
1723   PetscFunctionReturn(PETSC_SUCCESS);
1724 }
1725 
1726 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1727 {
1728   DMPolytopeType ct;
1729   PetscMPIInt    rank;
1730   PetscInt       cdim;
1731 
1732   PetscFunctionBegin;
1733   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1734   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1735   PetscCall(DMGetCoordinateDim(dm, &cdim));
1736   switch (ct) {
1737   case DM_POLYTOPE_SEGMENT:
1738   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1739     switch (cdim) {
1740     case 1: {
1741       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1742       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1743 
1744       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, PETSC_DRAW_BLACK));
1745       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, PETSC_DRAW_BLACK));
1746       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, PETSC_DRAW_BLACK));
1747     } break;
1748     case 2: {
1749       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1750       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1751       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1752 
1753       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1754       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));
1755       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));
1756     } break;
1757     default:
1758       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1759     }
1760     break;
1761   case DM_POLYTOPE_TRIANGLE:
1762     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));
1763     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1764     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1765     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1766     break;
1767   case DM_POLYTOPE_QUADRILATERAL:
1768     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));
1769     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));
1770     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1771     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1772     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1773     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1774     break;
1775   case DM_POLYTOPE_SEG_PRISM_TENSOR:
1776     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));
1777     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));
1778     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1779     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1780     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1781     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1782     break;
1783   case DM_POLYTOPE_FV_GHOST:
1784     break;
1785   default:
1786     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1787   }
1788   PetscFunctionReturn(PETSC_SUCCESS);
1789 }
1790 
1791 static PetscErrorCode DrawPolygon_Private(DM dm, PetscDraw draw, PetscInt cell, PetscInt Nv, const PetscReal refVertices[], const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1792 {
1793   PetscReal   centroid[2] = {0., 0.};
1794   PetscMPIInt rank;
1795   PetscInt    fillColor;
1796 
1797   PetscFunctionBegin;
1798   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1799   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1800   for (PetscInt v = 0; v < Nv; ++v) {
1801     centroid[0] += PetscRealPart(coords[v * 2 + 0]) / Nv;
1802     centroid[1] += PetscRealPart(coords[v * 2 + 1]) / Nv;
1803   }
1804   for (PetscInt e = 0; e < Nv; ++e) {
1805     refCoords[0] = refVertices[e * 2 + 0];
1806     refCoords[1] = refVertices[e * 2 + 1];
1807     for (PetscInt d = 1; d <= edgeDiv; ++d) {
1808       refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % Nv * 2 + 0] - refCoords[0]) * d / edgeDiv;
1809       refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % Nv * 2 + 1] - refCoords[1]) * d / edgeDiv;
1810     }
1811     PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1812     for (PetscInt d = 0; d < edgeDiv; ++d) {
1813       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));
1814       PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1815     }
1816   }
1817   PetscFunctionReturn(PETSC_SUCCESS);
1818 }
1819 
1820 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1821 {
1822   DMPolytopeType ct;
1823 
1824   PetscFunctionBegin;
1825   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1826   switch (ct) {
1827   case DM_POLYTOPE_TRIANGLE: {
1828     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1829 
1830     PetscCall(DrawPolygon_Private(dm, draw, cell, 3, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1831   } break;
1832   case DM_POLYTOPE_QUADRILATERAL: {
1833     PetscReal refVertices[8] = {-1., -1., 1., -1., 1., 1., -1., 1.};
1834 
1835     PetscCall(DrawPolygon_Private(dm, draw, cell, 4, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1836   } break;
1837   default:
1838     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1839   }
1840   PetscFunctionReturn(PETSC_SUCCESS);
1841 }
1842 
1843 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1844 {
1845   PetscDraw    draw;
1846   DM           cdm;
1847   PetscSection coordSection;
1848   Vec          coordinates;
1849   PetscReal    xyl[3], xyr[3];
1850   PetscReal   *refCoords, *edgeCoords;
1851   PetscBool    isnull, drawAffine;
1852   PetscInt     dim, vStart, vEnd, cStart, cEnd, c, cDegree, edgeDiv;
1853 
1854   PetscFunctionBegin;
1855   PetscCall(DMGetCoordinateDim(dm, &dim));
1856   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1857   PetscCall(DMGetCoordinateDegree_Internal(dm, &cDegree));
1858   drawAffine = cDegree > 1 ? PETSC_FALSE : PETSC_TRUE;
1859   edgeDiv    = cDegree + 1;
1860   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1861   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1862   PetscCall(DMGetCoordinateDM(dm, &cdm));
1863   PetscCall(DMGetLocalSection(cdm, &coordSection));
1864   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1865   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1866   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1867 
1868   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1869   PetscCall(PetscDrawIsNull(draw, &isnull));
1870   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
1871   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1872 
1873   PetscCall(DMGetBoundingBox(dm, xyl, xyr));
1874   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1875   PetscCall(PetscDrawClear(draw));
1876 
1877   for (c = cStart; c < cEnd; ++c) {
1878     PetscScalar       *coords = NULL;
1879     const PetscScalar *coords_arr;
1880     PetscInt           numCoords;
1881     PetscBool          isDG;
1882 
1883     PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1884     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1885     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1886     PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1887   }
1888   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1889   PetscCall(PetscDrawFlush(draw));
1890   PetscCall(PetscDrawPause(draw));
1891   PetscCall(PetscDrawSave(draw));
1892   PetscFunctionReturn(PETSC_SUCCESS);
1893 }
1894 
1895 static PetscErrorCode DMPlexCreateHighOrderSurrogate_Internal(DM dm, DM *hdm)
1896 {
1897   DM           odm = dm, rdm = dm, cdm;
1898   PetscFE      fe;
1899   PetscSpace   sp;
1900   PetscClassId id;
1901   PetscInt     degree;
1902   PetscBool    hoView = PETSC_TRUE;
1903 
1904   PetscFunctionBegin;
1905   PetscObjectOptionsBegin((PetscObject)dm);
1906   PetscCall(PetscOptionsBool("-dm_plex_high_order_view", "Subsample to view meshes with high order coordinates", "DMPlexCreateHighOrderSurrogate_Internal", hoView, &hoView, NULL));
1907   PetscOptionsEnd();
1908   PetscCall(PetscObjectReference((PetscObject)dm));
1909   *hdm = dm;
1910   if (!hoView) PetscFunctionReturn(PETSC_SUCCESS);
1911   PetscCall(DMGetCoordinateDM(dm, &cdm));
1912   PetscCall(DMGetField(cdm, 0, NULL, (PetscObject *)&fe));
1913   PetscCall(PetscObjectGetClassId((PetscObject)fe, &id));
1914   if (id != PETSCFE_CLASSID) PetscFunctionReturn(PETSC_SUCCESS);
1915   PetscCall(PetscFEGetBasisSpace(fe, &sp));
1916   PetscCall(PetscSpaceGetDegree(sp, &degree, NULL));
1917   for (PetscInt r = 0, rd = PetscCeilReal(((PetscReal)degree) / 2.); r < (PetscInt)PetscCeilReal(PetscLog2Real(degree)); ++r, rd = PetscCeilReal(((PetscReal)rd) / 2.)) {
1918     DM  cdm, rcdm;
1919     Mat In;
1920     Vec cl, rcl;
1921 
1922     PetscCall(DMRefine(odm, PetscObjectComm((PetscObject)odm), &rdm));
1923     PetscCall(DMPlexCreateCoordinateSpace(rdm, rd, PETSC_FALSE, NULL));
1924     PetscCall(PetscObjectSetName((PetscObject)rdm, "Refined Mesh with Linear Coordinates"));
1925     PetscCall(DMGetCoordinateDM(odm, &cdm));
1926     PetscCall(DMGetCoordinateDM(rdm, &rcdm));
1927     PetscCall(DMGetCoordinatesLocal(odm, &cl));
1928     PetscCall(DMGetCoordinatesLocal(rdm, &rcl));
1929     PetscCall(DMSetCoarseDM(rcdm, cdm));
1930     PetscCall(DMCreateInterpolation(cdm, rcdm, &In, NULL));
1931     PetscCall(MatMult(In, cl, rcl));
1932     PetscCall(MatDestroy(&In));
1933     PetscCall(DMSetCoordinatesLocal(rdm, rcl));
1934     PetscCall(DMDestroy(&odm));
1935     odm = rdm;
1936   }
1937   *hdm = rdm;
1938   PetscFunctionReturn(PETSC_SUCCESS);
1939 }
1940 
1941 #if defined(PETSC_HAVE_EXODUSII)
1942   #include <exodusII.h>
1943   #include <petscviewerexodusii.h>
1944 #endif
1945 
1946 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1947 {
1948   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns;
1949   char      name[PETSC_MAX_PATH_LEN];
1950 
1951   PetscFunctionBegin;
1952   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1953   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1954   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
1955   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
1956   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1957   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
1958   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
1959   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
1960   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
1961   if (iascii) {
1962     PetscViewerFormat format;
1963     PetscCall(PetscViewerGetFormat(viewer, &format));
1964     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1965     else PetscCall(DMPlexView_Ascii(dm, viewer));
1966   } else if (ishdf5) {
1967 #if defined(PETSC_HAVE_HDF5)
1968     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1969 #else
1970     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1971 #endif
1972   } else if (isvtk) {
1973     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
1974   } else if (isdraw) {
1975     DM hdm;
1976 
1977     PetscCall(DMPlexCreateHighOrderSurrogate_Internal(dm, &hdm));
1978     PetscCall(DMPlexView_Draw(hdm, viewer));
1979     PetscCall(DMDestroy(&hdm));
1980   } else if (isglvis) {
1981     PetscCall(DMPlexView_GLVis(dm, viewer));
1982 #if defined(PETSC_HAVE_EXODUSII)
1983   } else if (isexodus) {
1984     /*
1985       exodusII requires that all sets be part of exactly one cell set.
1986       If the dm does not have a "Cell Sets" label defined, we create one
1987       with ID 1, containing all cells.
1988       Note that if the Cell Sets label is defined but does not cover all cells,
1989       we may still have a problem. This should probably be checked here or in the viewer;
1990     */
1991     PetscInt numCS;
1992     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
1993     if (!numCS) {
1994       PetscInt cStart, cEnd, c;
1995       PetscCall(DMCreateLabel(dm, "Cell Sets"));
1996       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1997       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
1998     }
1999     PetscCall(DMView_PlexExodusII(dm, viewer));
2000 #endif
2001 #if defined(PETSC_HAVE_CGNS)
2002   } else if (iscgns) {
2003     PetscCall(DMView_PlexCGNS(dm, viewer));
2004 #endif
2005   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
2006   /* Optionally view the partition */
2007   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
2008   if (flg) {
2009     Vec ranks;
2010     PetscCall(DMPlexCreateRankField(dm, &ranks));
2011     PetscCall(VecView(ranks, viewer));
2012     PetscCall(VecDestroy(&ranks));
2013   }
2014   /* Optionally view a label */
2015   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
2016   if (flg) {
2017     DMLabel label;
2018     Vec     val;
2019 
2020     PetscCall(DMGetLabel(dm, name, &label));
2021     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
2022     PetscCall(DMPlexCreateLabelField(dm, label, &val));
2023     PetscCall(VecView(val, viewer));
2024     PetscCall(VecDestroy(&val));
2025   }
2026   PetscFunctionReturn(PETSC_SUCCESS);
2027 }
2028 
2029 /*@
2030   DMPlexTopologyView - Saves a `DMPLEX` topology into a file
2031 
2032   Collective
2033 
2034   Input Parameters:
2035 + dm     - The `DM` whose topology is to be saved
2036 - viewer - The `PetscViewer` to save it in
2037 
2038   Level: advanced
2039 
2040 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`, `PetscViewer`
2041 @*/
2042 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
2043 {
2044   PetscBool ishdf5;
2045 
2046   PetscFunctionBegin;
2047   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2048   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2049   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2050   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
2051   if (ishdf5) {
2052 #if defined(PETSC_HAVE_HDF5)
2053     PetscViewerFormat format;
2054     PetscCall(PetscViewerGetFormat(viewer, &format));
2055     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2056       IS globalPointNumbering;
2057 
2058       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2059       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
2060       PetscCall(ISDestroy(&globalPointNumbering));
2061     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2062 #else
2063     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2064 #endif
2065   }
2066   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
2067   PetscFunctionReturn(PETSC_SUCCESS);
2068 }
2069 
2070 /*@
2071   DMPlexCoordinatesView - Saves `DMPLEX` coordinates into a file
2072 
2073   Collective
2074 
2075   Input Parameters:
2076 + dm     - The `DM` whose coordinates are to be saved
2077 - viewer - The `PetscViewer` for saving
2078 
2079   Level: advanced
2080 
2081 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`, `PetscViewer`
2082 @*/
2083 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
2084 {
2085   PetscBool ishdf5;
2086 
2087   PetscFunctionBegin;
2088   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2089   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2090   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2091   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2092   if (ishdf5) {
2093 #if defined(PETSC_HAVE_HDF5)
2094     PetscViewerFormat format;
2095     PetscCall(PetscViewerGetFormat(viewer, &format));
2096     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2097       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
2098     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2099 #else
2100     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2101 #endif
2102   }
2103   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2104   PetscFunctionReturn(PETSC_SUCCESS);
2105 }
2106 
2107 /*@
2108   DMPlexLabelsView - Saves `DMPLEX` labels into a file
2109 
2110   Collective
2111 
2112   Input Parameters:
2113 + dm     - The `DM` whose labels are to be saved
2114 - viewer - The `PetscViewer` for saving
2115 
2116   Level: advanced
2117 
2118 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`, `PetscViewer`
2119 @*/
2120 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
2121 {
2122   PetscBool ishdf5;
2123 
2124   PetscFunctionBegin;
2125   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2126   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2127   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2128   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
2129   if (ishdf5) {
2130 #if defined(PETSC_HAVE_HDF5)
2131     IS                globalPointNumbering;
2132     PetscViewerFormat format;
2133 
2134     PetscCall(PetscViewerGetFormat(viewer, &format));
2135     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2136       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2137       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
2138       PetscCall(ISDestroy(&globalPointNumbering));
2139     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2140 #else
2141     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2142 #endif
2143   }
2144   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
2145   PetscFunctionReturn(PETSC_SUCCESS);
2146 }
2147 
2148 /*@
2149   DMPlexSectionView - Saves a section associated with a `DMPLEX`
2150 
2151   Collective
2152 
2153   Input Parameters:
2154 + dm        - The `DM` that contains the topology on which the section to be saved is defined
2155 . viewer    - The `PetscViewer` for saving
2156 - sectiondm - The `DM` that contains the section to be saved, can be `NULL`
2157 
2158   Level: advanced
2159 
2160   Notes:
2161   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.
2162 
2163   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.
2164 
2165 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`, `PetscViewer`
2166 @*/
2167 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
2168 {
2169   PetscBool ishdf5;
2170 
2171   PetscFunctionBegin;
2172   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2173   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2174   if (!sectiondm) sectiondm = dm;
2175   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2176   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2177   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
2178   if (ishdf5) {
2179 #if defined(PETSC_HAVE_HDF5)
2180     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
2181 #else
2182     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2183 #endif
2184   }
2185   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
2186   PetscFunctionReturn(PETSC_SUCCESS);
2187 }
2188 
2189 /*@
2190   DMPlexGlobalVectorView - Saves a global vector
2191 
2192   Collective
2193 
2194   Input Parameters:
2195 + dm        - The `DM` that represents the topology
2196 . viewer    - The `PetscViewer` to save data with
2197 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2198 - vec       - The global vector to be saved
2199 
2200   Level: advanced
2201 
2202   Notes:
2203   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.
2204 
2205   Calling sequence:
2206 .vb
2207        DMCreate(PETSC_COMM_WORLD, &dm);
2208        DMSetType(dm, DMPLEX);
2209        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2210        DMClone(dm, &sectiondm);
2211        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2212        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2213        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2214        PetscSectionSetChart(section, pStart, pEnd);
2215        PetscSectionSetUp(section);
2216        DMSetLocalSection(sectiondm, section);
2217        PetscSectionDestroy(&section);
2218        DMGetGlobalVector(sectiondm, &vec);
2219        PetscObjectSetName((PetscObject)vec, "vec_name");
2220        DMPlexTopologyView(dm, viewer);
2221        DMPlexSectionView(dm, viewer, sectiondm);
2222        DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
2223        DMRestoreGlobalVector(sectiondm, &vec);
2224        DMDestroy(&sectiondm);
2225        DMDestroy(&dm);
2226 .ve
2227 
2228 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2229 @*/
2230 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2231 {
2232   PetscBool ishdf5;
2233 
2234   PetscFunctionBegin;
2235   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2236   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2237   if (!sectiondm) sectiondm = dm;
2238   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2239   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2240   /* Check consistency */
2241   {
2242     PetscSection section;
2243     PetscBool    includesConstraints;
2244     PetscInt     m, m1;
2245 
2246     PetscCall(VecGetLocalSize(vec, &m1));
2247     PetscCall(DMGetGlobalSection(sectiondm, &section));
2248     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2249     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2250     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2251     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2252   }
2253   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2254   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2255   if (ishdf5) {
2256 #if defined(PETSC_HAVE_HDF5)
2257     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2258 #else
2259     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2260 #endif
2261   }
2262   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2263   PetscFunctionReturn(PETSC_SUCCESS);
2264 }
2265 
2266 /*@
2267   DMPlexLocalVectorView - Saves a local vector
2268 
2269   Collective
2270 
2271   Input Parameters:
2272 + dm        - The `DM` that represents the topology
2273 . viewer    - The `PetscViewer` to save data with
2274 . sectiondm - The `DM` that contains the local section on which `vec` is defined, can be `NULL`
2275 - vec       - The local vector to be saved
2276 
2277   Level: advanced
2278 
2279   Note:
2280   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.
2281 
2282   Calling sequence:
2283 .vb
2284        DMCreate(PETSC_COMM_WORLD, &dm);
2285        DMSetType(dm, DMPLEX);
2286        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2287        DMClone(dm, &sectiondm);
2288        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2289        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2290        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2291        PetscSectionSetChart(section, pStart, pEnd);
2292        PetscSectionSetUp(section);
2293        DMSetLocalSection(sectiondm, section);
2294        DMGetLocalVector(sectiondm, &vec);
2295        PetscObjectSetName((PetscObject)vec, "vec_name");
2296        DMPlexTopologyView(dm, viewer);
2297        DMPlexSectionView(dm, viewer, sectiondm);
2298        DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2299        DMRestoreLocalVector(sectiondm, &vec);
2300        DMDestroy(&sectiondm);
2301        DMDestroy(&dm);
2302 .ve
2303 
2304 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2305 @*/
2306 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2307 {
2308   PetscBool ishdf5;
2309 
2310   PetscFunctionBegin;
2311   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2312   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2313   if (!sectiondm) sectiondm = dm;
2314   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2315   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2316   /* Check consistency */
2317   {
2318     PetscSection section;
2319     PetscBool    includesConstraints;
2320     PetscInt     m, m1;
2321 
2322     PetscCall(VecGetLocalSize(vec, &m1));
2323     PetscCall(DMGetLocalSection(sectiondm, &section));
2324     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2325     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2326     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2327     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2328   }
2329   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2330   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2331   if (ishdf5) {
2332 #if defined(PETSC_HAVE_HDF5)
2333     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2334 #else
2335     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2336 #endif
2337   }
2338   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2339   PetscFunctionReturn(PETSC_SUCCESS);
2340 }
2341 
2342 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2343 {
2344   PetscBool ishdf5;
2345 
2346   PetscFunctionBegin;
2347   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2348   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2349   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2350   if (ishdf5) {
2351 #if defined(PETSC_HAVE_HDF5)
2352     PetscViewerFormat format;
2353     PetscCall(PetscViewerGetFormat(viewer, &format));
2354     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2355       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2356     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2357       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2358     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2359     PetscFunctionReturn(PETSC_SUCCESS);
2360 #else
2361     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2362 #endif
2363   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2364 }
2365 
2366 /*@
2367   DMPlexTopologyLoad - Loads a topology into a `DMPLEX`
2368 
2369   Collective
2370 
2371   Input Parameters:
2372 + dm     - The `DM` into which the topology is loaded
2373 - viewer - The `PetscViewer` for the saved topology
2374 
2375   Output Parameter:
2376 . 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;
2377   `NULL` if unneeded
2378 
2379   Level: advanced
2380 
2381 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2382           `PetscViewer`, `PetscSF`
2383 @*/
2384 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2385 {
2386   PetscBool ishdf5;
2387 
2388   PetscFunctionBegin;
2389   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2390   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2391   if (globalToLocalPointSF) PetscAssertPointer(globalToLocalPointSF, 3);
2392   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2393   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2394   if (ishdf5) {
2395 #if defined(PETSC_HAVE_HDF5)
2396     PetscViewerFormat format;
2397     PetscCall(PetscViewerGetFormat(viewer, &format));
2398     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2399       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2400     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2401 #else
2402     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2403 #endif
2404   }
2405   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2406   PetscFunctionReturn(PETSC_SUCCESS);
2407 }
2408 
2409 /*@
2410   DMPlexCoordinatesLoad - Loads coordinates into a `DMPLEX`
2411 
2412   Collective
2413 
2414   Input Parameters:
2415 + dm                   - The `DM` into which the coordinates are loaded
2416 . viewer               - The `PetscViewer` for the saved coordinates
2417 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2418 
2419   Level: advanced
2420 
2421 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2422           `PetscSF`, `PetscViewer`
2423 @*/
2424 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2425 {
2426   PetscBool ishdf5;
2427 
2428   PetscFunctionBegin;
2429   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2430   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2431   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2432   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2433   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2434   if (ishdf5) {
2435 #if defined(PETSC_HAVE_HDF5)
2436     PetscViewerFormat format;
2437     PetscCall(PetscViewerGetFormat(viewer, &format));
2438     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2439       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2440     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2441 #else
2442     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2443 #endif
2444   }
2445   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2446   PetscFunctionReturn(PETSC_SUCCESS);
2447 }
2448 
2449 /*@
2450   DMPlexLabelsLoad - Loads labels into a `DMPLEX`
2451 
2452   Collective
2453 
2454   Input Parameters:
2455 + dm                   - The `DM` into which the labels are loaded
2456 . viewer               - The `PetscViewer` for the saved labels
2457 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading `dm` from viewer
2458 
2459   Level: advanced
2460 
2461   Note:
2462   The `PetscSF` argument must not be `NULL` if the `DM` is distributed, otherwise an error occurs.
2463 
2464 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2465           `PetscSF`, `PetscViewer`
2466 @*/
2467 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2468 {
2469   PetscBool ishdf5;
2470 
2471   PetscFunctionBegin;
2472   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2473   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2474   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2475   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2476   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2477   if (ishdf5) {
2478 #if defined(PETSC_HAVE_HDF5)
2479     PetscViewerFormat format;
2480 
2481     PetscCall(PetscViewerGetFormat(viewer, &format));
2482     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2483       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2484     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2485 #else
2486     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2487 #endif
2488   }
2489   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2490   PetscFunctionReturn(PETSC_SUCCESS);
2491 }
2492 
2493 /*@
2494   DMPlexSectionLoad - Loads section into a `DMPLEX`
2495 
2496   Collective
2497 
2498   Input Parameters:
2499 + dm                   - The `DM` that represents the topology
2500 . viewer               - The `PetscViewer` that represents the on-disk section (sectionA)
2501 . sectiondm            - The `DM` into which the on-disk section (sectionA) is migrated, can be `NULL`
2502 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad(`) when loading dm from viewer
2503 
2504   Output Parameters:
2505 + 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)
2506 - 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)
2507 
2508   Level: advanced
2509 
2510   Notes:
2511   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.
2512 
2513   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.
2514 
2515   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.
2516 
2517   Example using 2 processes:
2518 .vb
2519   NX (number of points on dm): 4
2520   sectionA                   : the on-disk section
2521   vecA                       : a vector associated with sectionA
2522   sectionB                   : sectiondm's local section constructed in this function
2523   vecB (local)               : a vector associated with sectiondm's local section
2524   vecB (global)              : a vector associated with sectiondm's global section
2525 
2526                                      rank 0    rank 1
2527   vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2528   sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2529   sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2530   sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2531   [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2532   sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2533   sectionB->atlasDof             :     1 0 1 | 1 3
2534   sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2535   vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2536   vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2537 .ve
2538   where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2539 
2540 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`, `PetscSF`, `PetscViewer`
2541 @*/
2542 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
2543 {
2544   PetscBool ishdf5;
2545 
2546   PetscFunctionBegin;
2547   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2548   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2549   if (!sectiondm) sectiondm = dm;
2550   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2551   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2552   if (globalDofSF) PetscAssertPointer(globalDofSF, 5);
2553   if (localDofSF) PetscAssertPointer(localDofSF, 6);
2554   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2555   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2556   if (ishdf5) {
2557 #if defined(PETSC_HAVE_HDF5)
2558     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2559 #else
2560     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2561 #endif
2562   }
2563   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2564   PetscFunctionReturn(PETSC_SUCCESS);
2565 }
2566 
2567 /*@
2568   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2569 
2570   Collective
2571 
2572   Input Parameters:
2573 + dm        - The `DM` that represents the topology
2574 . viewer    - The `PetscViewer` that represents the on-disk vector data
2575 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2576 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2577 - vec       - The global vector to set values of
2578 
2579   Level: advanced
2580 
2581   Notes:
2582   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.
2583 
2584   Calling sequence:
2585 .vb
2586        DMCreate(PETSC_COMM_WORLD, &dm);
2587        DMSetType(dm, DMPLEX);
2588        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2589        DMPlexTopologyLoad(dm, viewer, &sfX);
2590        DMClone(dm, &sectiondm);
2591        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2592        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2593        DMGetGlobalVector(sectiondm, &vec);
2594        PetscObjectSetName((PetscObject)vec, "vec_name");
2595        DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2596        DMRestoreGlobalVector(sectiondm, &vec);
2597        PetscSFDestroy(&gsf);
2598        PetscSFDestroy(&sfX);
2599        DMDestroy(&sectiondm);
2600        DMDestroy(&dm);
2601 .ve
2602 
2603 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2604           `PetscSF`, `PetscViewer`
2605 @*/
2606 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2607 {
2608   PetscBool ishdf5;
2609 
2610   PetscFunctionBegin;
2611   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2612   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2613   if (!sectiondm) sectiondm = dm;
2614   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2615   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2616   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2617   /* Check consistency */
2618   {
2619     PetscSection section;
2620     PetscBool    includesConstraints;
2621     PetscInt     m, m1;
2622 
2623     PetscCall(VecGetLocalSize(vec, &m1));
2624     PetscCall(DMGetGlobalSection(sectiondm, &section));
2625     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2626     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2627     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2628     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2629   }
2630   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2631   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2632   if (ishdf5) {
2633 #if defined(PETSC_HAVE_HDF5)
2634     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2635 #else
2636     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2637 #endif
2638   }
2639   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2640   PetscFunctionReturn(PETSC_SUCCESS);
2641 }
2642 
2643 /*@
2644   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2645 
2646   Collective
2647 
2648   Input Parameters:
2649 + dm        - The `DM` that represents the topology
2650 . viewer    - The `PetscViewer` that represents the on-disk vector data
2651 . sectiondm - The `DM` that contains the local section on which vec is defined, can be `NULL`
2652 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2653 - vec       - The local vector to set values of
2654 
2655   Level: advanced
2656 
2657   Notes:
2658   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.
2659 
2660   Calling sequence:
2661 .vb
2662        DMCreate(PETSC_COMM_WORLD, &dm);
2663        DMSetType(dm, DMPLEX);
2664        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2665        DMPlexTopologyLoad(dm, viewer, &sfX);
2666        DMClone(dm, &sectiondm);
2667        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2668        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2669        DMGetLocalVector(sectiondm, &vec);
2670        PetscObjectSetName((PetscObject)vec, "vec_name");
2671        DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2672        DMRestoreLocalVector(sectiondm, &vec);
2673        PetscSFDestroy(&lsf);
2674        PetscSFDestroy(&sfX);
2675        DMDestroy(&sectiondm);
2676        DMDestroy(&dm);
2677 .ve
2678 
2679 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2680           `PetscSF`, `PetscViewer`
2681 @*/
2682 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2683 {
2684   PetscBool ishdf5;
2685 
2686   PetscFunctionBegin;
2687   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2688   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2689   if (!sectiondm) sectiondm = dm;
2690   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2691   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2692   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2693   /* Check consistency */
2694   {
2695     PetscSection section;
2696     PetscBool    includesConstraints;
2697     PetscInt     m, m1;
2698 
2699     PetscCall(VecGetLocalSize(vec, &m1));
2700     PetscCall(DMGetLocalSection(sectiondm, &section));
2701     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2702     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2703     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2704     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2705   }
2706   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2707   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2708   if (ishdf5) {
2709 #if defined(PETSC_HAVE_HDF5)
2710     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2711 #else
2712     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2713 #endif
2714   }
2715   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2716   PetscFunctionReturn(PETSC_SUCCESS);
2717 }
2718 
2719 PetscErrorCode DMDestroy_Plex(DM dm)
2720 {
2721   DM_Plex *mesh = (DM_Plex *)dm->data;
2722 
2723   PetscFunctionBegin;
2724   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2725   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2726   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2727   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2728   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerivativeBoundaryValues_C", NULL));
2729   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2730   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2731   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2732   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2733   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2734   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2735   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetDefault_C", NULL));
2736   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetDefault_C", NULL));
2737   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetType_C", NULL));
2738   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetType_C", NULL));
2739   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2740   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2741   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetUseCeed_C", NULL));
2742   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetUseCeed_C", NULL));
2743   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", NULL));
2744   if (--mesh->refct > 0) PetscFunctionReturn(PETSC_SUCCESS);
2745   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2746   PetscCall(PetscFree(mesh->cones));
2747   PetscCall(PetscFree(mesh->coneOrientations));
2748   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2749   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2750   PetscCall(PetscFree(mesh->supports));
2751   PetscCall(PetscFree(mesh->cellTypes));
2752   PetscCall(DMPlexTransformDestroy(&mesh->tr));
2753   PetscCall(PetscFree(mesh->tetgenOpts));
2754   PetscCall(PetscFree(mesh->triangleOpts));
2755   PetscCall(PetscFree(mesh->transformType));
2756   PetscCall(PetscFree(mesh->distributionName));
2757   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2758   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2759   PetscCall(ISDestroy(&mesh->subpointIS));
2760   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2761   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2762   if (mesh->periodic.face_sfs) {
2763     for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(PetscSFDestroy(&mesh->periodic.face_sfs[i]));
2764     PetscCall(PetscFree(mesh->periodic.face_sfs));
2765   }
2766   PetscCall(PetscSFDestroy(&mesh->periodic.composed_sf));
2767   if (mesh->periodic.periodic_points) {
2768     for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(ISDestroy(&mesh->periodic.periodic_points[i]));
2769     PetscCall(PetscFree(mesh->periodic.periodic_points));
2770   }
2771   if (mesh->periodic.transform) PetscCall(PetscFree(mesh->periodic.transform));
2772   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2773   PetscCall(ISDestroy(&mesh->anchorIS));
2774   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2775   PetscCall(PetscFree(mesh->parents));
2776   PetscCall(PetscFree(mesh->childIDs));
2777   PetscCall(PetscSectionDestroy(&mesh->childSection));
2778   PetscCall(PetscFree(mesh->children));
2779   PetscCall(DMDestroy(&mesh->referenceTree));
2780   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2781   PetscCall(PetscFree(mesh->neighbors));
2782   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2783   if (mesh->nonempty_comm != MPI_COMM_NULL && mesh->nonempty_comm != MPI_COMM_SELF) PetscCallMPI(MPI_Comm_free(&mesh->nonempty_comm));
2784   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2785   PetscCall(PetscFree(mesh));
2786   PetscFunctionReturn(PETSC_SUCCESS);
2787 }
2788 
2789 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2790 {
2791   PetscSection           sectionGlobal, sectionLocal;
2792   PetscInt               bs = -1, mbs;
2793   PetscInt               localSize, localStart = 0;
2794   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2795   MatType                mtype;
2796   ISLocalToGlobalMapping ltog;
2797 
2798   PetscFunctionBegin;
2799   PetscCall(MatInitializePackage());
2800   mtype = dm->mattype;
2801   PetscCall(DMGetLocalSection(dm, &sectionLocal));
2802   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2803   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2804   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2805   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2806   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2807   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2808   PetscCall(MatSetType(*J, mtype));
2809   PetscCall(MatSetFromOptions(*J));
2810   PetscCall(MatGetBlockSize(*J, &mbs));
2811   if (mbs > 1) bs = mbs;
2812   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2813   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2814   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2815   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2816   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2817   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2818   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2819   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2820   if (!isShell) {
2821     // There are three states with pblocks, since block starts can have no dofs:
2822     // UNKNOWN) New Block:   An open block has been signalled by pblocks[p] == 1
2823     // TRUE)    Block Start: The first entry in a block has been added
2824     // FALSE)   Block Add:   An additional block entry has been added, since pblocks[p] == 0
2825     PetscBT         blst;
2826     PetscBool3      bstate     = PETSC_BOOL3_UNKNOWN;
2827     PetscBool       fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2828     const PetscInt *perm       = NULL;
2829     PetscInt       *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2830     PetscInt        pStart, pEnd, dof, cdof, num_fields;
2831 
2832     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2833     PetscCall(PetscSectionGetBlockStarts(sectionLocal, &blst));
2834     if (sectionLocal->perm) PetscCall(ISGetIndices(sectionLocal->perm, &perm));
2835 
2836     PetscCall(PetscCalloc1(localSize, &pblocks));
2837     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2838     PetscCall(PetscSectionGetNumFields(sectionGlobal, &num_fields));
2839     // We need to process in the permuted order to get block sizes right
2840     for (PetscInt point = pStart; point < pEnd; ++point) {
2841       const PetscInt p = perm ? perm[point] : point;
2842 
2843       switch (dm->blocking_type) {
2844       case DM_BLOCKING_TOPOLOGICAL_POINT: { // One block per topological point
2845         PetscInt bdof, offset;
2846 
2847         PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2848         PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2849         PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2850         if (blst && PetscBTLookup(blst, p)) bstate = PETSC_BOOL3_UNKNOWN;
2851         if (dof > 0) {
2852           // State change
2853           if (bstate == PETSC_BOOL3_UNKNOWN) bstate = PETSC_BOOL3_TRUE;
2854           else if (bstate == PETSC_BOOL3_TRUE && blst && !PetscBTLookup(blst, p)) bstate = PETSC_BOOL3_FALSE;
2855 
2856           for (PetscInt i = 0; i < dof - cdof; ++i) pblocks[offset - localStart + i] = dof - cdof;
2857           // Signal block concatenation
2858           if (bstate == PETSC_BOOL3_FALSE && dof - cdof) pblocks[offset - localStart] = -(dof - cdof);
2859         }
2860         dof  = dof < 0 ? -(dof + 1) : dof;
2861         bdof = cdof && (dof - cdof) ? 1 : dof;
2862         if (dof) {
2863           if (bs < 0) {
2864             bs = bdof;
2865           } else if (bs != bdof) {
2866             bs = 1;
2867           }
2868         }
2869       } break;
2870       case DM_BLOCKING_FIELD_NODE: {
2871         for (PetscInt field = 0; field < num_fields; field++) {
2872           PetscInt num_comp, bdof, offset;
2873           PetscCall(PetscSectionGetFieldComponents(sectionGlobal, field, &num_comp));
2874           PetscCall(PetscSectionGetFieldDof(sectionGlobal, p, field, &dof));
2875           if (dof < 0) continue;
2876           PetscCall(PetscSectionGetFieldOffset(sectionGlobal, p, field, &offset));
2877           PetscCall(PetscSectionGetFieldConstraintDof(sectionGlobal, p, field, &cdof));
2878           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);
2879           PetscInt num_nodes = dof / num_comp;
2880           for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = (dof - cdof) / num_nodes;
2881           // Handle possibly constant block size (unlikely)
2882           bdof = cdof && (dof - cdof) ? 1 : dof;
2883           if (dof) {
2884             if (bs < 0) {
2885               bs = bdof;
2886             } else if (bs != bdof) {
2887               bs = 1;
2888             }
2889           }
2890         }
2891       } break;
2892       }
2893     }
2894     if (sectionLocal->perm) PetscCall(ISRestoreIndices(sectionLocal->perm, &perm));
2895     /* Must have same blocksize on all procs (some might have no points) */
2896     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2897     bsLocal[1] = bs;
2898     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
2899     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2900     else bs = bsMinMax[0];
2901     bs = PetscMax(1, bs);
2902     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
2903     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2904       PetscCall(MatSetBlockSize(*J, bs));
2905       PetscCall(MatSetUp(*J));
2906     } else {
2907       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
2908       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2909       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2910     }
2911     if (pblocks) { // Consolidate blocks
2912       PetscInt nblocks = 0;
2913       pblocks[0]       = PetscAbs(pblocks[0]);
2914       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
2915         if (pblocks[i] == 0) continue;
2916         // Negative block size indicates the blocks should be concatenated
2917         if (pblocks[i] < 0) {
2918           pblocks[i] = -pblocks[i];
2919           pblocks[nblocks - 1] += pblocks[i];
2920         } else {
2921           pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2922         }
2923         for (PetscInt j = 1; j < pblocks[i]; j++)
2924           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);
2925       }
2926       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2927     }
2928     PetscCall(PetscFree(pblocks));
2929   }
2930   PetscCall(MatSetDM(*J, dm));
2931   PetscFunctionReturn(PETSC_SUCCESS);
2932 }
2933 
2934 /*@
2935   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2936 
2937   Not Collective
2938 
2939   Input Parameter:
2940 . dm - The `DMPLEX`
2941 
2942   Output Parameter:
2943 . subsection - The subdomain section
2944 
2945   Level: developer
2946 
2947 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscSection`
2948 @*/
2949 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2950 {
2951   DM_Plex *mesh = (DM_Plex *)dm->data;
2952 
2953   PetscFunctionBegin;
2954   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2955   if (!mesh->subdomainSection) {
2956     PetscSection section;
2957     PetscSF      sf;
2958 
2959     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
2960     PetscCall(DMGetLocalSection(dm, &section));
2961     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
2962     PetscCall(PetscSFDestroy(&sf));
2963   }
2964   *subsection = mesh->subdomainSection;
2965   PetscFunctionReturn(PETSC_SUCCESS);
2966 }
2967 
2968 /*@
2969   DMPlexGetChart - Return the interval for all mesh points [`pStart`, `pEnd`)
2970 
2971   Not Collective
2972 
2973   Input Parameter:
2974 . dm - The `DMPLEX`
2975 
2976   Output Parameters:
2977 + pStart - The first mesh point
2978 - pEnd   - The upper bound for mesh points
2979 
2980   Level: beginner
2981 
2982 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`
2983 @*/
2984 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
2985 {
2986   DM_Plex *mesh = (DM_Plex *)dm->data;
2987 
2988   PetscFunctionBegin;
2989   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2990   if (mesh->tr) PetscCall(DMPlexTransformGetChart(mesh->tr, pStart, pEnd));
2991   else PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
2992   PetscFunctionReturn(PETSC_SUCCESS);
2993 }
2994 
2995 /*@
2996   DMPlexSetChart - Set the interval for all mesh points [`pStart`, `pEnd`)
2997 
2998   Not Collective
2999 
3000   Input Parameters:
3001 + dm     - The `DMPLEX`
3002 . pStart - The first mesh point
3003 - pEnd   - The upper bound for mesh points
3004 
3005   Level: beginner
3006 
3007 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetChart()`
3008 @*/
3009 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
3010 {
3011   DM_Plex *mesh = (DM_Plex *)dm->data;
3012 
3013   PetscFunctionBegin;
3014   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3015   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
3016   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
3017   PetscCall(PetscFree(mesh->cellTypes));
3018   PetscFunctionReturn(PETSC_SUCCESS);
3019 }
3020 
3021 /*@
3022   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
3023 
3024   Not Collective
3025 
3026   Input Parameters:
3027 + dm - The `DMPLEX`
3028 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3029 
3030   Output Parameter:
3031 . size - The cone size for point `p`
3032 
3033   Level: beginner
3034 
3035 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3036 @*/
3037 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
3038 {
3039   DM_Plex *mesh = (DM_Plex *)dm->data;
3040 
3041   PetscFunctionBegin;
3042   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3043   PetscAssertPointer(size, 3);
3044   if (mesh->tr) PetscCall(DMPlexTransformGetConeSize(mesh->tr, p, size));
3045   else PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
3046   PetscFunctionReturn(PETSC_SUCCESS);
3047 }
3048 
3049 /*@
3050   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
3051 
3052   Not Collective
3053 
3054   Input Parameters:
3055 + dm   - The `DMPLEX`
3056 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3057 - size - The cone size for point `p`
3058 
3059   Level: beginner
3060 
3061   Note:
3062   This should be called after `DMPlexSetChart()`.
3063 
3064 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
3065 @*/
3066 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
3067 {
3068   DM_Plex *mesh = (DM_Plex *)dm->data;
3069 
3070   PetscFunctionBegin;
3071   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3072   PetscCheck(!mesh->tr, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Cannot call DMPlexSetConeSize() on a mesh with a transform defined.");
3073   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
3074   PetscFunctionReturn(PETSC_SUCCESS);
3075 }
3076 
3077 /*@C
3078   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
3079 
3080   Not Collective
3081 
3082   Input Parameters:
3083 + dm - The `DMPLEX`
3084 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3085 
3086   Output Parameter:
3087 . cone - An array of points which are on the in-edges for point `p`, the length of `cone` is the result of `DMPlexGetConeSize()`
3088 
3089   Level: beginner
3090 
3091   Fortran Notes:
3092   `cone` must be declared with
3093 .vb
3094   PetscInt, pointer :: cone(:)
3095 .ve
3096 
3097   You must also call `DMPlexRestoreCone()` after you finish using the array.
3098   `DMPlexRestoreCone()` is not needed/available in C.
3099 
3100 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`, `DMPlexRestoreCone()`
3101 @*/
3102 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
3103 {
3104   DM_Plex *mesh = (DM_Plex *)dm->data;
3105   PetscInt off;
3106 
3107   PetscFunctionBegin;
3108   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3109   PetscAssertPointer(cone, 3);
3110   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3111   *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3112   PetscFunctionReturn(PETSC_SUCCESS);
3113 }
3114 
3115 /*@
3116   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
3117 
3118   Not Collective
3119 
3120   Input Parameters:
3121 + dm - The `DMPLEX`
3122 - p  - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3123 
3124   Output Parameters:
3125 + pConesSection - `PetscSection` describing the layout of `pCones`
3126 - pCones        - An `IS` containing the points which are on the in-edges for the point set `p`
3127 
3128   Level: intermediate
3129 
3130 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`, `PetscSection`, `IS`
3131 @*/
3132 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
3133 {
3134   PetscSection cs, newcs;
3135   PetscInt    *cones;
3136   PetscInt    *newarr = NULL;
3137   PetscInt     n;
3138 
3139   PetscFunctionBegin;
3140   PetscCall(DMPlexGetCones(dm, &cones));
3141   PetscCall(DMPlexGetConeSection(dm, &cs));
3142   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
3143   if (pConesSection) *pConesSection = newcs;
3144   if (pCones) {
3145     PetscCall(PetscSectionGetStorageSize(newcs, &n));
3146     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
3147   }
3148   PetscFunctionReturn(PETSC_SUCCESS);
3149 }
3150 
3151 /*@
3152   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
3153 
3154   Not Collective
3155 
3156   Input Parameters:
3157 + dm     - The `DMPLEX`
3158 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3159 
3160   Output Parameter:
3161 . expandedPoints - An `IS` containing the of vertices recursively expanded from input points
3162 
3163   Level: advanced
3164 
3165   Notes:
3166   Like `DMPlexGetConeRecursive()` but returns only the 0-depth `IS` (i.e. vertices only) and no sections.
3167 
3168   There is no corresponding Restore function, just call `ISDestroy()` on the returned `IS` to deallocate.
3169 
3170 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`,
3171           `DMPlexGetDepth()`, `IS`
3172 @*/
3173 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
3174 {
3175   IS      *expandedPointsAll;
3176   PetscInt depth;
3177 
3178   PetscFunctionBegin;
3179   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3180   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3181   PetscAssertPointer(expandedPoints, 3);
3182   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3183   *expandedPoints = expandedPointsAll[0];
3184   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
3185   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3186   PetscFunctionReturn(PETSC_SUCCESS);
3187 }
3188 
3189 /*@
3190   DMPlexGetConeRecursive - Expand each given point into its cone points and do that recursively until we end up just with vertices
3191   (DAG points of depth 0, i.e., without cones).
3192 
3193   Not Collective
3194 
3195   Input Parameters:
3196 + dm     - The `DMPLEX`
3197 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3198 
3199   Output Parameters:
3200 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3201 . expandedPoints - (optional) An array of index sets with recursively expanded cones
3202 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3203 
3204   Level: advanced
3205 
3206   Notes:
3207   Like `DMPlexGetConeTuple()` but recursive.
3208 
3209   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.
3210   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
3211 
3212   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\:
3213   (1) DAG points in `expandedPoints`[d+1] with `depth` d+1 to their cone points in `expandedPoints`[d];
3214   (2) DAG points in `expandedPoints`[d+1] with `depth` in [0,d] to the same points in `expandedPoints`[d].
3215 
3216 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3217           `DMPlexGetDepth()`, `PetscSection`, `IS`
3218 @*/
3219 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3220 {
3221   const PetscInt *arr0 = NULL, *cone = NULL;
3222   PetscInt       *arr = NULL, *newarr = NULL;
3223   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
3224   IS             *expandedPoints_;
3225   PetscSection   *sections_;
3226 
3227   PetscFunctionBegin;
3228   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3229   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3230   if (depth) PetscAssertPointer(depth, 3);
3231   if (expandedPoints) PetscAssertPointer(expandedPoints, 4);
3232   if (sections) PetscAssertPointer(sections, 5);
3233   PetscCall(ISGetLocalSize(points, &n));
3234   PetscCall(ISGetIndices(points, &arr0));
3235   PetscCall(DMPlexGetDepth(dm, &depth_));
3236   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
3237   PetscCall(PetscCalloc1(depth_, &sections_));
3238   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
3239   for (d = depth_ - 1; d >= 0; d--) {
3240     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
3241     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
3242     for (i = 0; i < n; i++) {
3243       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
3244       if (arr[i] >= start && arr[i] < end) {
3245         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
3246         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
3247       } else {
3248         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
3249       }
3250     }
3251     PetscCall(PetscSectionSetUp(sections_[d]));
3252     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
3253     PetscCall(PetscMalloc1(newn, &newarr));
3254     for (i = 0; i < n; i++) {
3255       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
3256       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
3257       if (cn > 1) {
3258         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
3259         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
3260       } else {
3261         newarr[co] = arr[i];
3262       }
3263     }
3264     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
3265     arr = newarr;
3266     n   = newn;
3267   }
3268   PetscCall(ISRestoreIndices(points, &arr0));
3269   *depth = depth_;
3270   if (expandedPoints) *expandedPoints = expandedPoints_;
3271   else {
3272     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
3273     PetscCall(PetscFree(expandedPoints_));
3274   }
3275   if (sections) *sections = sections_;
3276   else {
3277     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
3278     PetscCall(PetscFree(sections_));
3279   }
3280   PetscFunctionReturn(PETSC_SUCCESS);
3281 }
3282 
3283 /*@
3284   DMPlexRestoreConeRecursive - Deallocates arrays created by `DMPlexGetConeRecursive()`
3285 
3286   Not Collective
3287 
3288   Input Parameters:
3289 + dm     - The `DMPLEX`
3290 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3291 
3292   Output Parameters:
3293 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3294 . expandedPoints - (optional) An array of recursively expanded cones
3295 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3296 
3297   Level: advanced
3298 
3299   Note:
3300   See `DMPlexGetConeRecursive()`
3301 
3302 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3303           `DMPlexGetDepth()`, `IS`, `PetscSection`
3304 @*/
3305 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3306 {
3307   PetscInt d, depth_;
3308 
3309   PetscFunctionBegin;
3310   PetscCall(DMPlexGetDepth(dm, &depth_));
3311   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
3312   if (depth) *depth = 0;
3313   if (expandedPoints) {
3314     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
3315     PetscCall(PetscFree(*expandedPoints));
3316   }
3317   if (sections) {
3318     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
3319     PetscCall(PetscFree(*sections));
3320   }
3321   PetscFunctionReturn(PETSC_SUCCESS);
3322 }
3323 
3324 /*@
3325   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
3326 
3327   Not Collective
3328 
3329   Input Parameters:
3330 + dm   - The `DMPLEX`
3331 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3332 - cone - An array of points which are on the in-edges for point `p`, its length must have been previously provided with `DMPlexSetConeSize()`
3333 
3334   Level: beginner
3335 
3336   Note:
3337   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3338 
3339 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3340 @*/
3341 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3342 {
3343   DM_Plex *mesh = (DM_Plex *)dm->data;
3344   PetscInt dof, off, c;
3345 
3346   PetscFunctionBegin;
3347   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3348   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3349   if (dof) PetscAssertPointer(cone, 3);
3350   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3351   if (PetscDefined(USE_DEBUG)) {
3352     PetscInt pStart, pEnd;
3353     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3354     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);
3355     for (c = 0; c < dof; ++c) {
3356       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);
3357       mesh->cones[off + c] = cone[c];
3358     }
3359   } else {
3360     for (c = 0; c < dof; ++c) mesh->cones[off + c] = cone[c];
3361   }
3362   PetscFunctionReturn(PETSC_SUCCESS);
3363 }
3364 
3365 /*@C
3366   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3367 
3368   Not Collective
3369 
3370   Input Parameters:
3371 + dm - The `DMPLEX`
3372 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3373 
3374   Output Parameter:
3375 . coneOrientation - An array of orientations which are on the in-edges for point `p`. An orientation is an
3376                     integer giving the prescription for cone traversal. Its length is given by the result of `DMPlexSetConeSize()`
3377 
3378   Level: beginner
3379 
3380   Note:
3381   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3382   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3383   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3384   with the identity.
3385 
3386   Fortran Notes:
3387   You must call `DMPlexRestoreConeOrientation()` after you finish using the returned array.
3388   `DMPlexRestoreConeOrientation()` is not needed/available in C.
3389 
3390 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetConeSize()`, `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`,
3391           `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3392 @*/
3393 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3394 {
3395   DM_Plex *mesh = (DM_Plex *)dm->data;
3396   PetscInt off;
3397 
3398   PetscFunctionBegin;
3399   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3400   if (PetscDefined(USE_DEBUG)) {
3401     PetscInt dof;
3402     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3403     if (dof) PetscAssertPointer(coneOrientation, 3);
3404   }
3405   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3406 
3407   *coneOrientation = &mesh->coneOrientations[off];
3408   PetscFunctionReturn(PETSC_SUCCESS);
3409 }
3410 
3411 /*@
3412   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3413 
3414   Not Collective
3415 
3416   Input Parameters:
3417 + dm              - The `DMPLEX`
3418 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3419 - coneOrientation - An array of orientations. Its length is given by the result of `DMPlexSetConeSize()`
3420 
3421   Level: beginner
3422 
3423   Notes:
3424   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3425 
3426   The meaning of coneOrientation is detailed in `DMPlexGetConeOrientation()`.
3427 
3428 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3429 @*/
3430 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3431 {
3432   DM_Plex *mesh = (DM_Plex *)dm->data;
3433   PetscInt pStart, pEnd;
3434   PetscInt dof, off, c;
3435 
3436   PetscFunctionBegin;
3437   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3438   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3439   if (dof) PetscAssertPointer(coneOrientation, 3);
3440   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3441   if (PetscDefined(USE_DEBUG)) {
3442     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3443     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);
3444     for (c = 0; c < dof; ++c) {
3445       PetscInt cdof, o = coneOrientation[c];
3446 
3447       PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3448       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);
3449       mesh->coneOrientations[off + c] = o;
3450     }
3451   } else {
3452     for (c = 0; c < dof; ++c) mesh->coneOrientations[off + c] = coneOrientation[c];
3453   }
3454   PetscFunctionReturn(PETSC_SUCCESS);
3455 }
3456 
3457 /*@
3458   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3459 
3460   Not Collective
3461 
3462   Input Parameters:
3463 + dm        - The `DMPLEX`
3464 . p         - The point, which must lie in the chart set with `DMPlexSetChart()`
3465 . conePos   - The local index in the cone where the point should be put
3466 - conePoint - The mesh point to insert
3467 
3468   Level: beginner
3469 
3470 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3471 @*/
3472 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3473 {
3474   DM_Plex *mesh = (DM_Plex *)dm->data;
3475   PetscInt pStart, pEnd;
3476   PetscInt dof, off;
3477 
3478   PetscFunctionBegin;
3479   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3480   if (PetscDefined(USE_DEBUG)) {
3481     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3482     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);
3483     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);
3484     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3485     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);
3486   }
3487   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3488   mesh->cones[off + conePos] = conePoint;
3489   PetscFunctionReturn(PETSC_SUCCESS);
3490 }
3491 
3492 /*@
3493   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3494 
3495   Not Collective
3496 
3497   Input Parameters:
3498 + dm              - The `DMPLEX`
3499 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3500 . conePos         - The local index in the cone where the point should be put
3501 - coneOrientation - The point orientation to insert
3502 
3503   Level: beginner
3504 
3505   Note:
3506   The meaning of coneOrientation values is detailed in `DMPlexGetConeOrientation()`.
3507 
3508 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3509 @*/
3510 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3511 {
3512   DM_Plex *mesh = (DM_Plex *)dm->data;
3513   PetscInt pStart, pEnd;
3514   PetscInt dof, off;
3515 
3516   PetscFunctionBegin;
3517   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3518   if (PetscDefined(USE_DEBUG)) {
3519     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3520     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);
3521     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3522     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);
3523   }
3524   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3525   mesh->coneOrientations[off + conePos] = coneOrientation;
3526   PetscFunctionReturn(PETSC_SUCCESS);
3527 }
3528 
3529 /*@C
3530   DMPlexGetOrientedCone - Return the points and orientations on the in-edges for this point in the DAG
3531 
3532   Not collective
3533 
3534   Input Parameters:
3535 + dm - The DMPlex
3536 - p  - The point, which must lie in the chart set with DMPlexSetChart()
3537 
3538   Output Parameters:
3539 + cone - An array of points which are on the in-edges for point `p`
3540 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3541          integer giving the prescription for cone traversal.
3542 
3543   Level: beginner
3544 
3545   Notes:
3546   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3547   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3548   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3549   with the identity.
3550 
3551   You must also call `DMPlexRestoreOrientedCone()` after you finish using the returned array.
3552 
3553   Fortran Notes:
3554   `cone` and `ornt` must be declared with
3555 .vb
3556   PetscInt, pointer :: cone(:)
3557   PetscInt, pointer :: ornt(:)
3558 .ve
3559 
3560 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3561 @*/
3562 PetscErrorCode DMPlexGetOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3563 {
3564   DM_Plex *mesh = (DM_Plex *)dm->data;
3565 
3566   PetscFunctionBegin;
3567   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3568   if (mesh->tr) {
3569     PetscCall(DMPlexTransformGetCone(mesh->tr, p, cone, ornt));
3570   } else {
3571     PetscInt off;
3572     if (PetscDefined(USE_DEBUG)) {
3573       PetscInt dof;
3574       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3575       if (dof) {
3576         if (cone) PetscAssertPointer(cone, 3);
3577         if (ornt) PetscAssertPointer(ornt, 4);
3578       }
3579     }
3580     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3581     if (cone) *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3582     if (ornt) *ornt = PetscSafePointerPlusOffset(mesh->coneOrientations, off);
3583   }
3584   PetscFunctionReturn(PETSC_SUCCESS);
3585 }
3586 
3587 /*@C
3588   DMPlexRestoreOrientedCone - Restore the points and orientations on the in-edges for this point in the DAG obtained with `DMPlexGetOrientedCone()`
3589 
3590   Not Collective
3591 
3592   Input Parameters:
3593 + dm   - The DMPlex
3594 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3595 . cone - An array of points which are on the in-edges for point p
3596 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3597          integer giving the prescription for cone traversal.
3598 
3599   Level: beginner
3600 
3601 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3602 @*/
3603 PetscErrorCode DMPlexRestoreOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3604 {
3605   DM_Plex *mesh = (DM_Plex *)dm->data;
3606 
3607   PetscFunctionBegin;
3608   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3609   if (mesh->tr) PetscCall(DMPlexTransformRestoreCone(mesh->tr, p, cone, ornt));
3610   PetscFunctionReturn(PETSC_SUCCESS);
3611 }
3612 
3613 /*@
3614   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
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 
3622   Output Parameter:
3623 . size - The support size for point `p`
3624 
3625   Level: beginner
3626 
3627 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3628 @*/
3629 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3630 {
3631   DM_Plex *mesh = (DM_Plex *)dm->data;
3632 
3633   PetscFunctionBegin;
3634   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3635   PetscAssertPointer(size, 3);
3636   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3637   PetscFunctionReturn(PETSC_SUCCESS);
3638 }
3639 
3640 /*@
3641   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3642 
3643   Not Collective
3644 
3645   Input Parameters:
3646 + dm   - The `DMPLEX`
3647 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3648 - size - The support size for point `p`
3649 
3650   Level: beginner
3651 
3652   Note:
3653   This should be called after `DMPlexSetChart()`.
3654 
3655 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3656 @*/
3657 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3658 {
3659   DM_Plex *mesh = (DM_Plex *)dm->data;
3660 
3661   PetscFunctionBegin;
3662   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3663   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3664   PetscFunctionReturn(PETSC_SUCCESS);
3665 }
3666 
3667 /*@C
3668   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3669 
3670   Not Collective
3671 
3672   Input Parameters:
3673 + dm - The `DMPLEX`
3674 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3675 
3676   Output Parameter:
3677 . support - An array of points which are on the out-edges for point `p`, its length is that obtained from `DMPlexGetSupportSize()`
3678 
3679   Level: beginner
3680 
3681   Fortran Notes:
3682   `support` must be declared with
3683 .vb
3684   PetscInt, pointer :: support(:)
3685 .ve
3686 
3687   You must also call `DMPlexRestoreSupport()` after you finish using the returned array.
3688   `DMPlexRestoreSupport()` is not needed/available in C.
3689 
3690 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3691 @*/
3692 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3693 {
3694   DM_Plex *mesh = (DM_Plex *)dm->data;
3695   PetscInt off;
3696 
3697   PetscFunctionBegin;
3698   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3699   PetscAssertPointer(support, 3);
3700   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3701   *support = PetscSafePointerPlusOffset(mesh->supports, off);
3702   PetscFunctionReturn(PETSC_SUCCESS);
3703 }
3704 
3705 /*@
3706   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3707 
3708   Not Collective
3709 
3710   Input Parameters:
3711 + dm      - The `DMPLEX`
3712 . p       - The point, which must lie in the chart set with `DMPlexSetChart()`
3713 - support - An array of points which are on the out-edges for point `p`, its length is that obtained from `DMPlexGetSupportSize()`
3714 
3715   Level: beginner
3716 
3717   Note:
3718   This should be called after all calls to `DMPlexSetSupportSize()` and `DMSetUp()`.
3719 
3720 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3721 @*/
3722 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3723 {
3724   DM_Plex *mesh = (DM_Plex *)dm->data;
3725   PetscInt pStart, pEnd;
3726   PetscInt dof, off, c;
3727 
3728   PetscFunctionBegin;
3729   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3730   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3731   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3732   if (dof) PetscAssertPointer(support, 3);
3733   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3734   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);
3735   for (c = 0; c < dof; ++c) {
3736     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);
3737     mesh->supports[off + c] = support[c];
3738   }
3739   PetscFunctionReturn(PETSC_SUCCESS);
3740 }
3741 
3742 /*@
3743   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3744 
3745   Not Collective
3746 
3747   Input Parameters:
3748 + dm           - The `DMPLEX`
3749 . p            - The point, which must lie in the chart set with `DMPlexSetChart()`
3750 . supportPos   - The local index in the cone where the point should be put
3751 - supportPoint - The mesh point to insert
3752 
3753   Level: beginner
3754 
3755 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3756 @*/
3757 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3758 {
3759   DM_Plex *mesh = (DM_Plex *)dm->data;
3760   PetscInt pStart, pEnd;
3761   PetscInt dof, off;
3762 
3763   PetscFunctionBegin;
3764   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3765   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3766   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3767   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3768   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);
3769   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);
3770   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);
3771   mesh->supports[off + supportPos] = supportPoint;
3772   PetscFunctionReturn(PETSC_SUCCESS);
3773 }
3774 
3775 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3776 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3777 {
3778   switch (ct) {
3779   case DM_POLYTOPE_SEGMENT:
3780     if (o == -1) return -2;
3781     break;
3782   case DM_POLYTOPE_TRIANGLE:
3783     if (o == -3) return -1;
3784     if (o == -2) return -3;
3785     if (o == -1) return -2;
3786     break;
3787   case DM_POLYTOPE_QUADRILATERAL:
3788     if (o == -4) return -2;
3789     if (o == -3) return -1;
3790     if (o == -2) return -4;
3791     if (o == -1) return -3;
3792     break;
3793   default:
3794     return o;
3795   }
3796   return o;
3797 }
3798 
3799 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3800 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3801 {
3802   switch (ct) {
3803   case DM_POLYTOPE_SEGMENT:
3804     if ((o == -2) || (o == 1)) return -1;
3805     if (o == -1) return 0;
3806     break;
3807   case DM_POLYTOPE_TRIANGLE:
3808     if (o == -3) return -2;
3809     if (o == -2) return -1;
3810     if (o == -1) return -3;
3811     break;
3812   case DM_POLYTOPE_QUADRILATERAL:
3813     if (o == -4) return -2;
3814     if (o == -3) return -1;
3815     if (o == -2) return -4;
3816     if (o == -1) return -3;
3817     break;
3818   default:
3819     return o;
3820   }
3821   return o;
3822 }
3823 
3824 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3825 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3826 {
3827   PetscInt pStart, pEnd, p;
3828 
3829   PetscFunctionBegin;
3830   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3831   for (p = pStart; p < pEnd; ++p) {
3832     const PetscInt *cone, *ornt;
3833     PetscInt        coneSize, c;
3834 
3835     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3836     PetscCall(DMPlexGetCone(dm, p, &cone));
3837     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3838     for (c = 0; c < coneSize; ++c) {
3839       DMPolytopeType ct;
3840       const PetscInt o = ornt[c];
3841 
3842       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3843       switch (ct) {
3844       case DM_POLYTOPE_SEGMENT:
3845         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3846         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3847         break;
3848       case DM_POLYTOPE_TRIANGLE:
3849         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3850         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3851         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3852         break;
3853       case DM_POLYTOPE_QUADRILATERAL:
3854         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3855         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3856         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3857         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3858         break;
3859       default:
3860         break;
3861       }
3862     }
3863   }
3864   PetscFunctionReturn(PETSC_SUCCESS);
3865 }
3866 
3867 static inline PetscErrorCode DMPlexGetTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3868 {
3869   DM_Plex *mesh = (DM_Plex *)dm->data;
3870 
3871   PetscFunctionBeginHot;
3872   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3873     if (useCone) {
3874       PetscCall(DMPlexGetConeSize(dm, p, size));
3875       PetscCall(DMPlexGetOrientedCone(dm, p, arr, ornt));
3876     } else {
3877       PetscCall(DMPlexGetSupportSize(dm, p, size));
3878       PetscCall(DMPlexGetSupport(dm, p, arr));
3879     }
3880   } else {
3881     if (useCone) {
3882       const PetscSection s   = mesh->coneSection;
3883       const PetscInt     ps  = p - s->pStart;
3884       const PetscInt     off = s->atlasOff[ps];
3885 
3886       *size = s->atlasDof[ps];
3887       *arr  = mesh->cones + off;
3888       *ornt = mesh->coneOrientations + off;
3889     } else {
3890       const PetscSection s   = mesh->supportSection;
3891       const PetscInt     ps  = p - s->pStart;
3892       const PetscInt     off = s->atlasOff[ps];
3893 
3894       *size = s->atlasDof[ps];
3895       *arr  = mesh->supports + off;
3896     }
3897   }
3898   PetscFunctionReturn(PETSC_SUCCESS);
3899 }
3900 
3901 static inline PetscErrorCode DMPlexRestoreTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3902 {
3903   DM_Plex *mesh = (DM_Plex *)dm->data;
3904 
3905   PetscFunctionBeginHot;
3906   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3907     if (useCone) PetscCall(DMPlexRestoreOrientedCone(dm, p, arr, ornt));
3908   }
3909   PetscFunctionReturn(PETSC_SUCCESS);
3910 }
3911 
3912 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3913 {
3914   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3915   PetscInt       *closure;
3916   const PetscInt *tmp = NULL, *tmpO = NULL;
3917   PetscInt        off = 0, tmpSize, t;
3918 
3919   PetscFunctionBeginHot;
3920   if (ornt) {
3921     PetscCall(DMPlexGetCellType(dm, p, &ct));
3922     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;
3923   }
3924   if (*points) {
3925     closure = *points;
3926   } else {
3927     PetscInt maxConeSize, maxSupportSize;
3928     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3929     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
3930   }
3931   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3932   if (ct == DM_POLYTOPE_UNKNOWN) {
3933     closure[off++] = p;
3934     closure[off++] = 0;
3935     for (t = 0; t < tmpSize; ++t) {
3936       closure[off++] = tmp[t];
3937       closure[off++] = tmpO ? tmpO[t] : 0;
3938     }
3939   } else {
3940     const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, ornt);
3941 
3942     /* We assume that cells with a valid type have faces with a valid type */
3943     closure[off++] = p;
3944     closure[off++] = ornt;
3945     for (t = 0; t < tmpSize; ++t) {
3946       DMPolytopeType ft;
3947 
3948       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3949       closure[off++] = tmp[arr[t]];
3950       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3951     }
3952   }
3953   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3954   if (numPoints) *numPoints = tmpSize + 1;
3955   if (points) *points = closure;
3956   PetscFunctionReturn(PETSC_SUCCESS);
3957 }
3958 
3959 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
3960 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3961 {
3962   const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, o);
3963   const PetscInt *cone, *ornt;
3964   PetscInt       *pts, *closure = NULL;
3965   DMPolytopeType  ft;
3966   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3967   PetscInt        dim, coneSize, c, d, clSize, cl;
3968 
3969   PetscFunctionBeginHot;
3970   PetscCall(DMGetDimension(dm, &dim));
3971   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3972   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3973   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
3974   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
3975   maxSize       = PetscMax(coneSeries, supportSeries);
3976   if (*points) {
3977     pts = *points;
3978   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
3979   c        = 0;
3980   pts[c++] = point;
3981   pts[c++] = o;
3982   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
3983   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
3984   for (cl = 0; cl < clSize * 2; cl += 2) {
3985     pts[c++] = closure[cl];
3986     pts[c++] = closure[cl + 1];
3987   }
3988   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
3989   for (cl = 0; cl < clSize * 2; cl += 2) {
3990     pts[c++] = closure[cl];
3991     pts[c++] = closure[cl + 1];
3992   }
3993   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3994   for (d = 2; d < coneSize; ++d) {
3995     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
3996     pts[c++] = cone[arr[d * 2 + 0]];
3997     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
3998   }
3999   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
4000   if (dim >= 3) {
4001     for (d = 2; d < coneSize; ++d) {
4002       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
4003       const PetscInt *fcone, *fornt;
4004       PetscInt        fconeSize, fc, i;
4005 
4006       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
4007       const PetscInt *farr = DMPolytopeTypeGetArrangement(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
4008       PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
4009       for (fc = 0; fc < fconeSize; ++fc) {
4010         const PetscInt cp = fcone[farr[fc * 2 + 0]];
4011         const PetscInt co = farr[fc * 2 + 1];
4012 
4013         for (i = 0; i < c; i += 2)
4014           if (pts[i] == cp) break;
4015         if (i == c) {
4016           PetscCall(DMPlexGetCellType(dm, cp, &ft));
4017           pts[c++] = cp;
4018           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
4019         }
4020       }
4021       PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
4022     }
4023   }
4024   *numPoints = c / 2;
4025   *points    = pts;
4026   PetscFunctionReturn(PETSC_SUCCESS);
4027 }
4028 
4029 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4030 {
4031   DMPolytopeType ct;
4032   PetscInt      *closure, *fifo;
4033   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
4034   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
4035   PetscInt       depth, maxSize;
4036 
4037   PetscFunctionBeginHot;
4038   PetscCall(DMPlexGetDepth(dm, &depth));
4039   if (depth == 1) {
4040     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
4041     PetscFunctionReturn(PETSC_SUCCESS);
4042   }
4043   PetscCall(DMPlexGetCellType(dm, p, &ct));
4044   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;
4045   if (DMPolytopeTypeIsHybrid(ct) && ct != DM_POLYTOPE_POINT_PRISM_TENSOR) {
4046     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
4047     PetscFunctionReturn(PETSC_SUCCESS);
4048   }
4049   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
4050   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
4051   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
4052   maxSize       = PetscMax(coneSeries, supportSeries);
4053   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
4054   if (*points) {
4055     closure = *points;
4056   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
4057   closure[closureSize++] = p;
4058   closure[closureSize++] = ornt;
4059   fifo[fifoSize++]       = p;
4060   fifo[fifoSize++]       = ornt;
4061   fifo[fifoSize++]       = ct;
4062   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
4063   while (fifoSize - fifoStart) {
4064     const PetscInt       q    = fifo[fifoStart++];
4065     const PetscInt       o    = fifo[fifoStart++];
4066     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
4067     const PetscInt      *qarr = DMPolytopeTypeGetArrangement(qt, o);
4068     const PetscInt      *tmp, *tmpO = NULL;
4069     PetscInt             tmpSize, t;
4070 
4071     if (PetscDefined(USE_DEBUG)) {
4072       PetscInt nO = DMPolytopeTypeGetNumArrangements(qt) / 2;
4073       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);
4074     }
4075     PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4076     for (t = 0; t < tmpSize; ++t) {
4077       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
4078       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
4079       const PetscInt cp = tmp[ip];
4080       PetscCall(DMPlexGetCellType(dm, cp, &ct));
4081       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
4082       PetscInt       c;
4083 
4084       /* Check for duplicate */
4085       for (c = 0; c < closureSize; c += 2) {
4086         if (closure[c] == cp) break;
4087       }
4088       if (c == closureSize) {
4089         closure[closureSize++] = cp;
4090         closure[closureSize++] = co;
4091         fifo[fifoSize++]       = cp;
4092         fifo[fifoSize++]       = co;
4093         fifo[fifoSize++]       = ct;
4094       }
4095     }
4096     PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4097   }
4098   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
4099   if (numPoints) *numPoints = closureSize / 2;
4100   if (points) *points = closure;
4101   PetscFunctionReturn(PETSC_SUCCESS);
4102 }
4103 
4104 /*@C
4105   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
4106 
4107   Not Collective
4108 
4109   Input Parameters:
4110 + dm      - The `DMPLEX`
4111 . p       - The mesh point
4112 - useCone - `PETSC_TRUE` for the closure, otherwise return the star
4113 
4114   Input/Output Parameter:
4115 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
4116            if *points is `NULL` on input, internal storage will be returned, use `DMPlexRestoreTransitiveClosure()`,
4117            otherwise the provided array is used to hold the values
4118 
4119   Output Parameter:
4120 . numPoints - The number of points in the closure, so `points` is of size 2*`numPoints`
4121 
4122   Level: beginner
4123 
4124   Note:
4125   If using internal storage (points is `NULL` on input), each call overwrites the last output.
4126 
4127   Fortran Notes:
4128   `points` must be declared with
4129 .vb
4130   PetscInt, pointer :: points(:)
4131 .ve
4132   and is always allocated by the function.
4133 
4134   The `numPoints` argument is not present in the Fortran binding.
4135 
4136 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4137 @*/
4138 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4139 {
4140   PetscFunctionBeginHot;
4141   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4142   if (numPoints) PetscAssertPointer(numPoints, 4);
4143   if (points) PetscAssertPointer(points, 5);
4144   if (PetscDefined(USE_DEBUG)) {
4145     PetscInt pStart, pEnd;
4146     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4147     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);
4148   }
4149   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
4150   PetscFunctionReturn(PETSC_SUCCESS);
4151 }
4152 
4153 /*@C
4154   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
4155 
4156   Not Collective
4157 
4158   Input Parameters:
4159 + dm        - The `DMPLEX`
4160 . p         - The mesh point
4161 . useCone   - `PETSC_TRUE` for the closure, otherwise return the star
4162 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
4163 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
4164 
4165   Level: beginner
4166 
4167   Note:
4168   If not using internal storage (points is not `NULL` on input), this call is unnecessary
4169 
4170 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4171 @*/
4172 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4173 {
4174   PetscFunctionBeginHot;
4175   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4176   if (numPoints) *numPoints = 0;
4177   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
4178   PetscFunctionReturn(PETSC_SUCCESS);
4179 }
4180 
4181 /*@
4182   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
4183 
4184   Not Collective
4185 
4186   Input Parameter:
4187 . dm - The `DMPLEX`
4188 
4189   Output Parameters:
4190 + maxConeSize    - The maximum number of in-edges
4191 - maxSupportSize - The maximum number of out-edges
4192 
4193   Level: beginner
4194 
4195 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
4196 @*/
4197 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
4198 {
4199   DM_Plex *mesh = (DM_Plex *)dm->data;
4200 
4201   PetscFunctionBegin;
4202   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4203   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
4204   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
4205   PetscFunctionReturn(PETSC_SUCCESS);
4206 }
4207 
4208 PetscErrorCode DMSetUp_Plex(DM dm)
4209 {
4210   DM_Plex *mesh = (DM_Plex *)dm->data;
4211   PetscInt size, maxSupportSize;
4212 
4213   PetscFunctionBegin;
4214   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4215   PetscCall(PetscSectionSetUp(mesh->coneSection));
4216   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
4217   PetscCall(PetscMalloc1(size, &mesh->cones));
4218   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
4219   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4220   if (maxSupportSize) {
4221     PetscCall(PetscSectionSetUp(mesh->supportSection));
4222     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
4223     PetscCall(PetscMalloc1(size, &mesh->supports));
4224   }
4225   PetscFunctionReturn(PETSC_SUCCESS);
4226 }
4227 
4228 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
4229 {
4230   PetscFunctionBegin;
4231   if (subdm) PetscCall(DMClone(dm, subdm));
4232   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, NULL, NULL, is, subdm));
4233   if (subdm) (*subdm)->useNatural = dm->useNatural;
4234   if (dm->useNatural && dm->sfMigration) {
4235     PetscSF sfNatural;
4236 
4237     (*subdm)->sfMigration = dm->sfMigration;
4238     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
4239     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
4240     (*subdm)->sfNatural = sfNatural;
4241   }
4242   PetscFunctionReturn(PETSC_SUCCESS);
4243 }
4244 
4245 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
4246 {
4247   PetscInt i = 0;
4248 
4249   PetscFunctionBegin;
4250   PetscCall(DMClone(dms[0], superdm));
4251   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
4252   (*superdm)->useNatural = PETSC_FALSE;
4253   for (i = 0; i < len; i++) {
4254     if (dms[i]->useNatural && dms[i]->sfMigration) {
4255       PetscSF sfNatural;
4256 
4257       (*superdm)->sfMigration = dms[i]->sfMigration;
4258       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
4259       (*superdm)->useNatural = PETSC_TRUE;
4260       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
4261       (*superdm)->sfNatural = sfNatural;
4262       break;
4263     }
4264   }
4265   PetscFunctionReturn(PETSC_SUCCESS);
4266 }
4267 
4268 /*@
4269   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
4270 
4271   Not Collective
4272 
4273   Input Parameter:
4274 . dm - The `DMPLEX`
4275 
4276   Level: beginner
4277 
4278   Note:
4279   This should be called after all calls to `DMPlexSetCone()`
4280 
4281 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
4282 @*/
4283 PetscErrorCode DMPlexSymmetrize(DM dm)
4284 {
4285   DM_Plex  *mesh = (DM_Plex *)dm->data;
4286   PetscInt *offsets;
4287   PetscInt  supportSize;
4288   PetscInt  pStart, pEnd, p;
4289 
4290   PetscFunctionBegin;
4291   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4292   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
4293   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
4294   /* Calculate support sizes */
4295   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4296   for (p = pStart; p < pEnd; ++p) {
4297     PetscInt dof, off, c;
4298 
4299     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4300     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4301     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
4302   }
4303   PetscCall(PetscSectionSetUp(mesh->supportSection));
4304   /* Calculate supports */
4305   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
4306   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
4307   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
4308   for (p = pStart; p < pEnd; ++p) {
4309     PetscInt dof, off, c;
4310 
4311     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4312     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4313     for (c = off; c < off + dof; ++c) {
4314       const PetscInt q = mesh->cones[c];
4315       PetscInt       offS;
4316 
4317       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
4318 
4319       mesh->supports[offS + offsets[q]] = p;
4320       ++offsets[q];
4321     }
4322   }
4323   PetscCall(PetscFree(offsets));
4324   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
4325   PetscFunctionReturn(PETSC_SUCCESS);
4326 }
4327 
4328 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
4329 {
4330   IS stratumIS;
4331 
4332   PetscFunctionBegin;
4333   if (pStart >= pEnd) PetscFunctionReturn(PETSC_SUCCESS);
4334   if (PetscDefined(USE_DEBUG)) {
4335     PetscInt  qStart, qEnd, numLevels, level;
4336     PetscBool overlap = PETSC_FALSE;
4337     PetscCall(DMLabelGetNumValues(label, &numLevels));
4338     for (level = 0; level < numLevels; level++) {
4339       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4340       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
4341         overlap = PETSC_TRUE;
4342         break;
4343       }
4344     }
4345     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);
4346   }
4347   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
4348   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
4349   PetscCall(ISDestroy(&stratumIS));
4350   PetscFunctionReturn(PETSC_SUCCESS);
4351 }
4352 
4353 static PetscErrorCode DMPlexStratify_CellType_Private(DM dm, DMLabel label)
4354 {
4355   PetscInt *pMin, *pMax;
4356   PetscInt  pStart, pEnd;
4357   PetscInt  dmin = PETSC_MAX_INT, dmax = PETSC_MIN_INT;
4358 
4359   PetscFunctionBegin;
4360   {
4361     DMLabel label2;
4362 
4363     PetscCall(DMPlexGetCellTypeLabel(dm, &label2));
4364     PetscCall(PetscObjectViewFromOptions((PetscObject)label2, NULL, "-ct_view"));
4365   }
4366   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4367   for (PetscInt p = pStart; p < pEnd; ++p) {
4368     DMPolytopeType ct;
4369 
4370     PetscCall(DMPlexGetCellType(dm, p, &ct));
4371     dmin = PetscMin(DMPolytopeTypeGetDim(ct), dmin);
4372     dmax = PetscMax(DMPolytopeTypeGetDim(ct), dmax);
4373   }
4374   PetscCall(PetscMalloc2(dmax + 1, &pMin, dmax + 1, &pMax));
4375   for (PetscInt d = dmin; d <= dmax; ++d) {
4376     pMin[d] = PETSC_MAX_INT;
4377     pMax[d] = PETSC_MIN_INT;
4378   }
4379   for (PetscInt p = pStart; p < pEnd; ++p) {
4380     DMPolytopeType ct;
4381     PetscInt       d;
4382 
4383     PetscCall(DMPlexGetCellType(dm, p, &ct));
4384     d       = DMPolytopeTypeGetDim(ct);
4385     pMin[d] = PetscMin(p, pMin[d]);
4386     pMax[d] = PetscMax(p, pMax[d]);
4387   }
4388   for (PetscInt d = dmin; d <= dmax; ++d) {
4389     if (pMin[d] > pMax[d]) continue;
4390     PetscCall(DMPlexCreateDepthStratum(dm, label, d, pMin[d], pMax[d] + 1));
4391   }
4392   PetscCall(PetscFree2(pMin, pMax));
4393   PetscFunctionReturn(PETSC_SUCCESS);
4394 }
4395 
4396 static PetscErrorCode DMPlexStratify_Topological_Private(DM dm, DMLabel label)
4397 {
4398   PetscInt pStart, pEnd;
4399   PetscInt numRoots = 0, numLeaves = 0;
4400 
4401   PetscFunctionBegin;
4402   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4403   {
4404     /* Initialize roots and count leaves */
4405     PetscInt sMin = PETSC_MAX_INT;
4406     PetscInt sMax = PETSC_MIN_INT;
4407     PetscInt coneSize, supportSize;
4408 
4409     for (PetscInt p = pStart; p < pEnd; ++p) {
4410       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4411       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4412       if (!coneSize && supportSize) {
4413         sMin = PetscMin(p, sMin);
4414         sMax = PetscMax(p, sMax);
4415         ++numRoots;
4416       } else if (!supportSize && coneSize) {
4417         ++numLeaves;
4418       } else if (!supportSize && !coneSize) {
4419         /* Isolated points */
4420         sMin = PetscMin(p, sMin);
4421         sMax = PetscMax(p, sMax);
4422       }
4423     }
4424     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4425   }
4426 
4427   if (numRoots + numLeaves == (pEnd - pStart)) {
4428     PetscInt sMin = PETSC_MAX_INT;
4429     PetscInt sMax = PETSC_MIN_INT;
4430     PetscInt coneSize, supportSize;
4431 
4432     for (PetscInt p = pStart; p < pEnd; ++p) {
4433       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4434       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4435       if (!supportSize && coneSize) {
4436         sMin = PetscMin(p, sMin);
4437         sMax = PetscMax(p, sMax);
4438       }
4439     }
4440     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4441   } else {
4442     PetscInt level = 0;
4443     PetscInt qStart, qEnd;
4444 
4445     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4446     while (qEnd > qStart) {
4447       PetscInt sMin = PETSC_MAX_INT;
4448       PetscInt sMax = PETSC_MIN_INT;
4449 
4450       for (PetscInt q = qStart; q < qEnd; ++q) {
4451         const PetscInt *support;
4452         PetscInt        supportSize;
4453 
4454         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4455         PetscCall(DMPlexGetSupport(dm, q, &support));
4456         for (PetscInt s = 0; s < supportSize; ++s) {
4457           sMin = PetscMin(support[s], sMin);
4458           sMax = PetscMax(support[s], sMax);
4459         }
4460       }
4461       PetscCall(DMLabelGetNumValues(label, &level));
4462       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4463       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4464     }
4465   }
4466   PetscFunctionReturn(PETSC_SUCCESS);
4467 }
4468 
4469 /*@
4470   DMPlexStratify - Computes the strata for all points in the `DMPLEX`
4471 
4472   Collective
4473 
4474   Input Parameter:
4475 . dm - The `DMPLEX`
4476 
4477   Level: beginner
4478 
4479   Notes:
4480   The strata group all points of the same grade, and this function calculates the strata. This
4481   grade can be seen as the height (or depth) of the point in the DAG.
4482 
4483   The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
4484   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram).
4485   Concretely, `DMPlexStratify()` creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
4486   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
4487   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through `DMPlexGetDepthLabel()` or `DMPlexGetDepthStratum()`, or
4488   manually via `DMGetLabel()`.  The height is defined implicitly by height = maxDimension - depth, and can be accessed
4489   via `DMPlexGetHeightStratum()`.  For example, cells have height 0 and faces have height 1.
4490 
4491   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
4492   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
4493   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
4494   to interpolate only that one (e0), so that
4495 .vb
4496   cone(c0) = {e0, v2}
4497   cone(e0) = {v0, v1}
4498 .ve
4499   If `DMPlexStratify()` is run on this mesh, it will give depths
4500 .vb
4501    depth 0 = {v0, v1, v2}
4502    depth 1 = {e0, c0}
4503 .ve
4504   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
4505 
4506   `DMPlexStratify()` should be called after all calls to `DMPlexSymmetrize()`
4507 
4508 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
4509 @*/
4510 PetscErrorCode DMPlexStratify(DM dm)
4511 {
4512   DM_Plex  *mesh = (DM_Plex *)dm->data;
4513   DMLabel   label;
4514   PetscBool flg = PETSC_FALSE;
4515 
4516   PetscFunctionBegin;
4517   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4518   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
4519 
4520   // Create depth label
4521   PetscCall(DMRemoveLabel(dm, "depth", NULL));
4522   PetscCall(DMCreateLabel(dm, "depth"));
4523   PetscCall(DMPlexGetDepthLabel(dm, &label));
4524 
4525   PetscCall(PetscOptionsGetBool(NULL, dm->hdr.prefix, "-dm_plex_stratify_celltype", &flg, NULL));
4526   if (flg) PetscCall(DMPlexStratify_CellType_Private(dm, label));
4527   else PetscCall(DMPlexStratify_Topological_Private(dm, label));
4528 
4529   { /* just in case there is an empty process */
4530     PetscInt numValues, maxValues = 0, v;
4531 
4532     PetscCall(DMLabelGetNumValues(label, &numValues));
4533     PetscCall(MPIU_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4534     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4535   }
4536   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4537   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4538   PetscFunctionReturn(PETSC_SUCCESS);
4539 }
4540 
4541 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4542 {
4543   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4544   PetscInt       dim, depth, pheight, coneSize;
4545 
4546   PetscFunctionBeginHot;
4547   PetscCall(DMGetDimension(dm, &dim));
4548   PetscCall(DMPlexGetDepth(dm, &depth));
4549   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4550   pheight = depth - pdepth;
4551   if (depth <= 1) {
4552     switch (pdepth) {
4553     case 0:
4554       ct = DM_POLYTOPE_POINT;
4555       break;
4556     case 1:
4557       switch (coneSize) {
4558       case 2:
4559         ct = DM_POLYTOPE_SEGMENT;
4560         break;
4561       case 3:
4562         ct = DM_POLYTOPE_TRIANGLE;
4563         break;
4564       case 4:
4565         switch (dim) {
4566         case 2:
4567           ct = DM_POLYTOPE_QUADRILATERAL;
4568           break;
4569         case 3:
4570           ct = DM_POLYTOPE_TETRAHEDRON;
4571           break;
4572         default:
4573           break;
4574         }
4575         break;
4576       case 5:
4577         ct = DM_POLYTOPE_PYRAMID;
4578         break;
4579       case 6:
4580         ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4581         break;
4582       case 8:
4583         ct = DM_POLYTOPE_HEXAHEDRON;
4584         break;
4585       default:
4586         break;
4587       }
4588     }
4589   } else {
4590     if (pdepth == 0) {
4591       ct = DM_POLYTOPE_POINT;
4592     } else if (pheight == 0) {
4593       switch (dim) {
4594       case 1:
4595         switch (coneSize) {
4596         case 2:
4597           ct = DM_POLYTOPE_SEGMENT;
4598           break;
4599         default:
4600           break;
4601         }
4602         break;
4603       case 2:
4604         switch (coneSize) {
4605         case 3:
4606           ct = DM_POLYTOPE_TRIANGLE;
4607           break;
4608         case 4:
4609           ct = DM_POLYTOPE_QUADRILATERAL;
4610           break;
4611         default:
4612           break;
4613         }
4614         break;
4615       case 3:
4616         switch (coneSize) {
4617         case 4:
4618           ct = DM_POLYTOPE_TETRAHEDRON;
4619           break;
4620         case 5: {
4621           const PetscInt *cone;
4622           PetscInt        faceConeSize;
4623 
4624           PetscCall(DMPlexGetCone(dm, p, &cone));
4625           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4626           switch (faceConeSize) {
4627           case 3:
4628             ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4629             break;
4630           case 4:
4631             ct = DM_POLYTOPE_PYRAMID;
4632             break;
4633           }
4634         } break;
4635         case 6:
4636           ct = DM_POLYTOPE_HEXAHEDRON;
4637           break;
4638         default:
4639           break;
4640         }
4641         break;
4642       default:
4643         break;
4644       }
4645     } else if (pheight > 0) {
4646       switch (coneSize) {
4647       case 2:
4648         ct = DM_POLYTOPE_SEGMENT;
4649         break;
4650       case 3:
4651         ct = DM_POLYTOPE_TRIANGLE;
4652         break;
4653       case 4:
4654         ct = DM_POLYTOPE_QUADRILATERAL;
4655         break;
4656       default:
4657         break;
4658       }
4659     }
4660   }
4661   *pt = ct;
4662   PetscFunctionReturn(PETSC_SUCCESS);
4663 }
4664 
4665 /*@
4666   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4667 
4668   Collective
4669 
4670   Input Parameter:
4671 . dm - The `DMPLEX`
4672 
4673   Level: developer
4674 
4675   Note:
4676   This function is normally called automatically when a cell type is requested. It creates an
4677   internal `DMLabel` named "celltype" which can be directly accessed using `DMGetLabel()`. A user may disable
4678   automatic creation by creating the label manually, using `DMCreateLabel`(dm, "celltype").
4679 
4680   `DMPlexComputeCellTypes()` should be called after all calls to `DMPlexSymmetrize()` and `DMPlexStratify()`
4681 
4682 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4683 @*/
4684 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4685 {
4686   DM_Plex *mesh;
4687   DMLabel  ctLabel;
4688   PetscInt pStart, pEnd, p;
4689 
4690   PetscFunctionBegin;
4691   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4692   mesh = (DM_Plex *)dm->data;
4693   PetscCall(DMCreateLabel(dm, "celltype"));
4694   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4695   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4696   PetscCall(PetscFree(mesh->cellTypes));
4697   PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
4698   for (p = pStart; p < pEnd; ++p) {
4699     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4700     PetscInt       pdepth;
4701 
4702     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4703     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4704     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]);
4705     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4706     mesh->cellTypes[p - pStart].value_as_uint8 = ct;
4707   }
4708   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4709   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4710   PetscFunctionReturn(PETSC_SUCCESS);
4711 }
4712 
4713 /*@C
4714   DMPlexGetJoin - Get an array for the join of the set of points
4715 
4716   Not Collective
4717 
4718   Input Parameters:
4719 + dm        - The `DMPLEX` object
4720 . numPoints - The number of input points for the join
4721 - points    - The input points
4722 
4723   Output Parameters:
4724 + numCoveredPoints - The number of points in the join
4725 - coveredPoints    - The points in the join
4726 
4727   Level: intermediate
4728 
4729   Note:
4730   Currently, this is restricted to a single level join
4731 
4732   Fortran Notes:
4733   `converedPoints` must be declared with
4734 .vb
4735   PetscInt, pointer :: coveredPints(:)
4736 .ve
4737 
4738   The `numCoveredPoints` argument is not present in the Fortran binding.
4739 
4740 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4741 @*/
4742 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4743 {
4744   DM_Plex  *mesh = (DM_Plex *)dm->data;
4745   PetscInt *join[2];
4746   PetscInt  joinSize, i = 0;
4747   PetscInt  dof, off, p, c, m;
4748   PetscInt  maxSupportSize;
4749 
4750   PetscFunctionBegin;
4751   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4752   PetscAssertPointer(points, 3);
4753   PetscAssertPointer(numCoveredPoints, 4);
4754   PetscAssertPointer(coveredPoints, 5);
4755   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4756   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4757   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4758   /* Copy in support of first point */
4759   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4760   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4761   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4762   /* Check each successive support */
4763   for (p = 1; p < numPoints; ++p) {
4764     PetscInt newJoinSize = 0;
4765 
4766     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4767     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4768     for (c = 0; c < dof; ++c) {
4769       const PetscInt point = mesh->supports[off + c];
4770 
4771       for (m = 0; m < joinSize; ++m) {
4772         if (point == join[i][m]) {
4773           join[1 - i][newJoinSize++] = point;
4774           break;
4775         }
4776       }
4777     }
4778     joinSize = newJoinSize;
4779     i        = 1 - i;
4780   }
4781   *numCoveredPoints = joinSize;
4782   *coveredPoints    = join[i];
4783   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4784   PetscFunctionReturn(PETSC_SUCCESS);
4785 }
4786 
4787 /*@C
4788   DMPlexRestoreJoin - Restore an array for the join of the set of points obtained with `DMPlexGetJoin()`
4789 
4790   Not Collective
4791 
4792   Input Parameters:
4793 + dm        - The `DMPLEX` object
4794 . numPoints - The number of input points for the join
4795 - points    - The input points
4796 
4797   Output Parameters:
4798 + numCoveredPoints - The number of points in the join
4799 - coveredPoints    - The points in the join
4800 
4801   Level: intermediate
4802 
4803   Fortran Notes:
4804   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4805 
4806 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4807 @*/
4808 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4809 {
4810   PetscFunctionBegin;
4811   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4812   if (points) PetscAssertPointer(points, 3);
4813   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4814   PetscAssertPointer(coveredPoints, 5);
4815   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4816   if (numCoveredPoints) *numCoveredPoints = 0;
4817   PetscFunctionReturn(PETSC_SUCCESS);
4818 }
4819 
4820 /*@C
4821   DMPlexGetFullJoin - Get an array for the join of the set of points
4822 
4823   Not Collective
4824 
4825   Input Parameters:
4826 + dm        - The `DMPLEX` object
4827 . numPoints - The number of input points for the join
4828 - points    - The input points, its length is `numPoints`
4829 
4830   Output Parameters:
4831 + numCoveredPoints - The number of points in the join
4832 - coveredPoints    - The points in the join, its length is `numCoveredPoints`
4833 
4834   Level: intermediate
4835 
4836   Fortran Notes:
4837   `points` and `converedPoints` must be declared with
4838 .vb
4839   PetscInt, pointer :: points(:)
4840   PetscInt, pointer :: coveredPints(:)
4841 .ve
4842 
4843   The `numCoveredPoints` argument is not present in the Fortran binding.
4844 
4845 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4846 @*/
4847 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4848 {
4849   PetscInt *offsets, **closures;
4850   PetscInt *join[2];
4851   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4852   PetscInt  p, d, c, m, ms;
4853 
4854   PetscFunctionBegin;
4855   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4856   PetscAssertPointer(points, 3);
4857   PetscAssertPointer(numCoveredPoints, 4);
4858   PetscAssertPointer(coveredPoints, 5);
4859 
4860   PetscCall(DMPlexGetDepth(dm, &depth));
4861   PetscCall(PetscCalloc1(numPoints, &closures));
4862   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4863   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4864   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4865   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4866   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4867 
4868   for (p = 0; p < numPoints; ++p) {
4869     PetscInt closureSize;
4870 
4871     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4872 
4873     offsets[p * (depth + 2) + 0] = 0;
4874     for (d = 0; d < depth + 1; ++d) {
4875       PetscInt pStart, pEnd, i;
4876 
4877       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4878       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4879         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4880           offsets[p * (depth + 2) + d + 1] = i;
4881           break;
4882         }
4883       }
4884       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4885     }
4886     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);
4887   }
4888   for (d = 0; d < depth + 1; ++d) {
4889     PetscInt dof;
4890 
4891     /* Copy in support of first point */
4892     dof = offsets[d + 1] - offsets[d];
4893     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4894     /* Check each successive cone */
4895     for (p = 1; p < numPoints && joinSize; ++p) {
4896       PetscInt newJoinSize = 0;
4897 
4898       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
4899       for (c = 0; c < dof; ++c) {
4900         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
4901 
4902         for (m = 0; m < joinSize; ++m) {
4903           if (point == join[i][m]) {
4904             join[1 - i][newJoinSize++] = point;
4905             break;
4906           }
4907         }
4908       }
4909       joinSize = newJoinSize;
4910       i        = 1 - i;
4911     }
4912     if (joinSize) break;
4913   }
4914   *numCoveredPoints = joinSize;
4915   *coveredPoints    = join[i];
4916   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4917   PetscCall(PetscFree(closures));
4918   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4919   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
4920   PetscFunctionReturn(PETSC_SUCCESS);
4921 }
4922 
4923 /*@C
4924   DMPlexGetMeet - Get an array for the meet of the set of points
4925 
4926   Not Collective
4927 
4928   Input Parameters:
4929 + dm        - The `DMPLEX` object
4930 . numPoints - The number of input points for the meet
4931 - points    - The input points, of length `numPoints`
4932 
4933   Output Parameters:
4934 + numCoveringPoints - The number of points in the meet
4935 - coveringPoints    - The points in the meet, of length `numCoveringPoints`
4936 
4937   Level: intermediate
4938 
4939   Note:
4940   Currently, this is restricted to a single level meet
4941 
4942   Fortran Notes:
4943   `coveringPoints` must be declared with
4944 .vb
4945   PetscInt, pointer :: coveringPoints(:)
4946 .ve
4947 
4948   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4949 
4950 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4951 @*/
4952 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt *coveringPoints[])
4953 {
4954   DM_Plex  *mesh = (DM_Plex *)dm->data;
4955   PetscInt *meet[2];
4956   PetscInt  meetSize, i = 0;
4957   PetscInt  dof, off, p, c, m;
4958   PetscInt  maxConeSize;
4959 
4960   PetscFunctionBegin;
4961   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4962   PetscAssertPointer(points, 3);
4963   PetscAssertPointer(numCoveringPoints, 4);
4964   PetscAssertPointer(coveringPoints, 5);
4965   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4966   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4967   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4968   /* Copy in cone of first point */
4969   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4970   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4971   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
4972   /* Check each successive cone */
4973   for (p = 1; p < numPoints; ++p) {
4974     PetscInt newMeetSize = 0;
4975 
4976     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4977     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4978     for (c = 0; c < dof; ++c) {
4979       const PetscInt point = mesh->cones[off + c];
4980 
4981       for (m = 0; m < meetSize; ++m) {
4982         if (point == meet[i][m]) {
4983           meet[1 - i][newMeetSize++] = point;
4984           break;
4985         }
4986       }
4987     }
4988     meetSize = newMeetSize;
4989     i        = 1 - i;
4990   }
4991   *numCoveringPoints = meetSize;
4992   *coveringPoints    = meet[i];
4993   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
4994   PetscFunctionReturn(PETSC_SUCCESS);
4995 }
4996 
4997 /*@C
4998   DMPlexRestoreMeet - Restore an array for the meet of the set of points obtained with `DMPlexGetMeet()`
4999 
5000   Not Collective
5001 
5002   Input Parameters:
5003 + dm        - The `DMPLEX` object
5004 . numPoints - The number of input points for the meet
5005 - points    - The input points
5006 
5007   Output Parameters:
5008 + numCoveredPoints - The number of points in the meet
5009 - coveredPoints    - The points in the meet
5010 
5011   Level: intermediate
5012 
5013   Fortran Notes:
5014   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
5015 
5016 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
5017 @*/
5018 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
5019 {
5020   PetscFunctionBegin;
5021   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5022   if (points) PetscAssertPointer(points, 3);
5023   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
5024   PetscAssertPointer(coveredPoints, 5);
5025   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
5026   if (numCoveredPoints) *numCoveredPoints = 0;
5027   PetscFunctionReturn(PETSC_SUCCESS);
5028 }
5029 
5030 /*@C
5031   DMPlexGetFullMeet - Get an array for the meet of the set of points
5032 
5033   Not Collective
5034 
5035   Input Parameters:
5036 + dm        - The `DMPLEX` object
5037 . numPoints - The number of input points for the meet
5038 - points    - The input points, of length  `numPoints`
5039 
5040   Output Parameters:
5041 + numCoveredPoints - The number of points in the meet
5042 - coveredPoints    - The points in the meet, of length  `numCoveredPoints`
5043 
5044   Level: intermediate
5045 
5046   Fortran Notes:
5047   `points` and `coveredPoints` must be declared with
5048 .vb
5049   PetscInt, pointer :: points(:)
5050   PetscInt, pointer :: coveredPoints(:)
5051 .ve
5052 
5053   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
5054 
5055 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
5056 @*/
5057 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
5058 {
5059   PetscInt *offsets, **closures;
5060   PetscInt *meet[2];
5061   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
5062   PetscInt  p, h, c, m, mc;
5063 
5064   PetscFunctionBegin;
5065   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5066   PetscAssertPointer(points, 3);
5067   PetscAssertPointer(numCoveredPoints, 4);
5068   PetscAssertPointer(coveredPoints, 5);
5069 
5070   PetscCall(DMPlexGetDepth(dm, &height));
5071   PetscCall(PetscMalloc1(numPoints, &closures));
5072   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5073   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
5074   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
5075   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
5076   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
5077 
5078   for (p = 0; p < numPoints; ++p) {
5079     PetscInt closureSize;
5080 
5081     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
5082 
5083     offsets[p * (height + 2) + 0] = 0;
5084     for (h = 0; h < height + 1; ++h) {
5085       PetscInt pStart, pEnd, i;
5086 
5087       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
5088       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
5089         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
5090           offsets[p * (height + 2) + h + 1] = i;
5091           break;
5092         }
5093       }
5094       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
5095     }
5096     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);
5097   }
5098   for (h = 0; h < height + 1; ++h) {
5099     PetscInt dof;
5100 
5101     /* Copy in cone of first point */
5102     dof = offsets[h + 1] - offsets[h];
5103     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
5104     /* Check each successive cone */
5105     for (p = 1; p < numPoints && meetSize; ++p) {
5106       PetscInt newMeetSize = 0;
5107 
5108       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
5109       for (c = 0; c < dof; ++c) {
5110         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
5111 
5112         for (m = 0; m < meetSize; ++m) {
5113           if (point == meet[i][m]) {
5114             meet[1 - i][newMeetSize++] = point;
5115             break;
5116           }
5117         }
5118       }
5119       meetSize = newMeetSize;
5120       i        = 1 - i;
5121     }
5122     if (meetSize) break;
5123   }
5124   *numCoveredPoints = meetSize;
5125   *coveredPoints    = meet[i];
5126   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
5127   PetscCall(PetscFree(closures));
5128   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5129   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
5130   PetscFunctionReturn(PETSC_SUCCESS);
5131 }
5132 
5133 /*@
5134   DMPlexEqual - Determine if two `DM` have the same topology
5135 
5136   Not Collective
5137 
5138   Input Parameters:
5139 + dmA - A `DMPLEX` object
5140 - dmB - A `DMPLEX` object
5141 
5142   Output Parameter:
5143 . equal - `PETSC_TRUE` if the topologies are identical
5144 
5145   Level: intermediate
5146 
5147   Note:
5148   We are not solving graph isomorphism, so we do not permute.
5149 
5150 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5151 @*/
5152 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
5153 {
5154   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
5155 
5156   PetscFunctionBegin;
5157   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
5158   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
5159   PetscAssertPointer(equal, 3);
5160 
5161   *equal = PETSC_FALSE;
5162   PetscCall(DMPlexGetDepth(dmA, &depth));
5163   PetscCall(DMPlexGetDepth(dmB, &depthB));
5164   if (depth != depthB) PetscFunctionReturn(PETSC_SUCCESS);
5165   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
5166   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
5167   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(PETSC_SUCCESS);
5168   for (p = pStart; p < pEnd; ++p) {
5169     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
5170     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
5171 
5172     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
5173     PetscCall(DMPlexGetCone(dmA, p, &cone));
5174     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
5175     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
5176     PetscCall(DMPlexGetCone(dmB, p, &coneB));
5177     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
5178     if (coneSize != coneSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5179     for (c = 0; c < coneSize; ++c) {
5180       if (cone[c] != coneB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5181       if (ornt[c] != orntB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5182     }
5183     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
5184     PetscCall(DMPlexGetSupport(dmA, p, &support));
5185     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
5186     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
5187     if (supportSize != supportSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5188     for (s = 0; s < supportSize; ++s) {
5189       if (support[s] != supportB[s]) PetscFunctionReturn(PETSC_SUCCESS);
5190     }
5191   }
5192   *equal = PETSC_TRUE;
5193   PetscFunctionReturn(PETSC_SUCCESS);
5194 }
5195 
5196 /*@
5197   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
5198 
5199   Not Collective
5200 
5201   Input Parameters:
5202 + dm         - The `DMPLEX`
5203 . cellDim    - The cell dimension
5204 - numCorners - The number of vertices on a cell
5205 
5206   Output Parameter:
5207 . numFaceVertices - The number of vertices on a face
5208 
5209   Level: developer
5210 
5211   Note:
5212   Of course this can only work for a restricted set of symmetric shapes
5213 
5214 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5215 @*/
5216 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
5217 {
5218   MPI_Comm comm;
5219 
5220   PetscFunctionBegin;
5221   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
5222   PetscAssertPointer(numFaceVertices, 4);
5223   switch (cellDim) {
5224   case 0:
5225     *numFaceVertices = 0;
5226     break;
5227   case 1:
5228     *numFaceVertices = 1;
5229     break;
5230   case 2:
5231     switch (numCorners) {
5232     case 3:                 /* triangle */
5233       *numFaceVertices = 2; /* Edge has 2 vertices */
5234       break;
5235     case 4:                 /* quadrilateral */
5236       *numFaceVertices = 2; /* Edge has 2 vertices */
5237       break;
5238     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
5239       *numFaceVertices = 3; /* Edge has 3 vertices */
5240       break;
5241     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
5242       *numFaceVertices = 3; /* Edge has 3 vertices */
5243       break;
5244     default:
5245       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5246     }
5247     break;
5248   case 3:
5249     switch (numCorners) {
5250     case 4:                 /* tetradehdron */
5251       *numFaceVertices = 3; /* Face has 3 vertices */
5252       break;
5253     case 6:                 /* tet cohesive cells */
5254       *numFaceVertices = 4; /* Face has 4 vertices */
5255       break;
5256     case 8:                 /* hexahedron */
5257       *numFaceVertices = 4; /* Face has 4 vertices */
5258       break;
5259     case 9:                 /* tet cohesive Lagrange cells */
5260       *numFaceVertices = 6; /* Face has 6 vertices */
5261       break;
5262     case 10:                /* quadratic tetrahedron */
5263       *numFaceVertices = 6; /* Face has 6 vertices */
5264       break;
5265     case 12:                /* hex cohesive Lagrange cells */
5266       *numFaceVertices = 6; /* Face has 6 vertices */
5267       break;
5268     case 18:                /* quadratic tet cohesive Lagrange cells */
5269       *numFaceVertices = 6; /* Face has 6 vertices */
5270       break;
5271     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
5272       *numFaceVertices = 9; /* Face has 9 vertices */
5273       break;
5274     default:
5275       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5276     }
5277     break;
5278   default:
5279     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
5280   }
5281   PetscFunctionReturn(PETSC_SUCCESS);
5282 }
5283 
5284 /*@
5285   DMPlexGetDepthLabel - Get the `DMLabel` recording the depth of each point
5286 
5287   Not Collective
5288 
5289   Input Parameter:
5290 . dm - The `DMPLEX` object
5291 
5292   Output Parameter:
5293 . depthLabel - The `DMLabel` recording point depth
5294 
5295   Level: developer
5296 
5297 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
5298 @*/
5299 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
5300 {
5301   PetscFunctionBegin;
5302   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5303   PetscAssertPointer(depthLabel, 2);
5304   *depthLabel = dm->depthLabel;
5305   PetscFunctionReturn(PETSC_SUCCESS);
5306 }
5307 
5308 /*@
5309   DMPlexGetDepth - Get the depth of the DAG representing this mesh
5310 
5311   Not Collective
5312 
5313   Input Parameter:
5314 . dm - The `DMPLEX` object
5315 
5316   Output Parameter:
5317 . depth - The number of strata (breadth first levels) in the DAG
5318 
5319   Level: developer
5320 
5321   Notes:
5322   This returns maximum of point depths over all points, i.e. maximum value of the label returned by `DMPlexGetDepthLabel()`.
5323 
5324   The point depth is described more in detail in `DMPlexGetDepthStratum()`.
5325 
5326   An empty mesh gives -1.
5327 
5328 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
5329 @*/
5330 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
5331 {
5332   DM_Plex *mesh = (DM_Plex *)dm->data;
5333   DMLabel  label;
5334   PetscInt d = -1;
5335 
5336   PetscFunctionBegin;
5337   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5338   PetscAssertPointer(depth, 2);
5339   if (mesh->tr) {
5340     PetscCall(DMPlexTransformGetDepth(mesh->tr, depth));
5341   } else {
5342     PetscCall(DMPlexGetDepthLabel(dm, &label));
5343     // Allow missing depths
5344     if (label) PetscCall(DMLabelGetValueBounds(label, NULL, &d));
5345     *depth = d;
5346   }
5347   PetscFunctionReturn(PETSC_SUCCESS);
5348 }
5349 
5350 /*@
5351   DMPlexGetDepthStratum - Get the bounds [`start`, `end`) for all points at a certain depth.
5352 
5353   Not Collective
5354 
5355   Input Parameters:
5356 + dm    - The `DMPLEX` object
5357 - depth - The requested depth
5358 
5359   Output Parameters:
5360 + start - The first point at this `depth`
5361 - end   - One beyond the last point at this `depth`
5362 
5363   Level: developer
5364 
5365   Notes:
5366   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
5367   often "vertices".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then depth stratum 1 contains the next
5368   higher dimension, e.g., "edges".
5369 
5370 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetHeightStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
5371 @*/
5372 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
5373 {
5374   DM_Plex *mesh = (DM_Plex *)dm->data;
5375   DMLabel  label;
5376   PetscInt pStart, pEnd;
5377 
5378   PetscFunctionBegin;
5379   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5380   if (start) {
5381     PetscAssertPointer(start, 3);
5382     *start = 0;
5383   }
5384   if (end) {
5385     PetscAssertPointer(end, 4);
5386     *end = 0;
5387   }
5388   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5389   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5390   if (depth < 0) {
5391     if (start) *start = pStart;
5392     if (end) *end = pEnd;
5393     PetscFunctionReturn(PETSC_SUCCESS);
5394   }
5395   if (mesh->tr) {
5396     PetscCall(DMPlexTransformGetDepthStratum(mesh->tr, depth, start, end));
5397   } else {
5398     PetscCall(DMPlexGetDepthLabel(dm, &label));
5399     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5400     PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
5401   }
5402   PetscFunctionReturn(PETSC_SUCCESS);
5403 }
5404 
5405 /*@
5406   DMPlexGetHeightStratum - Get the bounds [`start`, `end`) for all points at a certain height.
5407 
5408   Not Collective
5409 
5410   Input Parameters:
5411 + dm     - The `DMPLEX` object
5412 - height - The requested height
5413 
5414   Output Parameters:
5415 + start - The first point at this `height`
5416 - end   - One beyond the last point at this `height`
5417 
5418   Level: developer
5419 
5420   Notes:
5421   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
5422   points, often called "cells" or "elements".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then height
5423   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
5424 
5425 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5426 @*/
5427 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
5428 {
5429   DMLabel  label;
5430   PetscInt depth, pStart, pEnd;
5431 
5432   PetscFunctionBegin;
5433   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5434   if (start) {
5435     PetscAssertPointer(start, 3);
5436     *start = 0;
5437   }
5438   if (end) {
5439     PetscAssertPointer(end, 4);
5440     *end = 0;
5441   }
5442   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5443   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5444   if (height < 0) {
5445     if (start) *start = pStart;
5446     if (end) *end = pEnd;
5447     PetscFunctionReturn(PETSC_SUCCESS);
5448   }
5449   PetscCall(DMPlexGetDepthLabel(dm, &label));
5450   if (label) PetscCall(DMLabelGetNumValues(label, &depth));
5451   else PetscCall(DMGetDimension(dm, &depth));
5452   PetscCheck(depth >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Depth not yet computed");
5453   PetscCall(DMPlexGetDepthStratum(dm, depth - 1 - height, start, end));
5454   PetscFunctionReturn(PETSC_SUCCESS);
5455 }
5456 
5457 /*@
5458   DMPlexGetPointDepth - Get the `depth` of a given point
5459 
5460   Not Collective
5461 
5462   Input Parameters:
5463 + dm    - The `DMPLEX` object
5464 - point - The point
5465 
5466   Output Parameter:
5467 . depth - The depth of the `point`
5468 
5469   Level: intermediate
5470 
5471 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5472 @*/
5473 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
5474 {
5475   PetscFunctionBegin;
5476   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5477   PetscAssertPointer(depth, 3);
5478   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
5479   PetscFunctionReturn(PETSC_SUCCESS);
5480 }
5481 
5482 /*@
5483   DMPlexGetPointHeight - Get the `height` of a given point
5484 
5485   Not Collective
5486 
5487   Input Parameters:
5488 + dm    - The `DMPLEX` object
5489 - point - The point
5490 
5491   Output Parameter:
5492 . height - The height of the `point`
5493 
5494   Level: intermediate
5495 
5496 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
5497 @*/
5498 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5499 {
5500   PetscInt n, pDepth;
5501 
5502   PetscFunctionBegin;
5503   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5504   PetscAssertPointer(height, 3);
5505   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5506   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5507   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5508   PetscFunctionReturn(PETSC_SUCCESS);
5509 }
5510 
5511 /*@
5512   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5513 
5514   Not Collective
5515 
5516   Input Parameter:
5517 . dm - The `DMPLEX` object
5518 
5519   Output Parameter:
5520 . celltypeLabel - The `DMLabel` recording cell polytope type
5521 
5522   Level: developer
5523 
5524   Note:
5525   This function will trigger automatica computation of cell types. This can be disabled by calling
5526   `DMCreateLabel`(dm, "celltype") beforehand.
5527 
5528 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5529 @*/
5530 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5531 {
5532   PetscFunctionBegin;
5533   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5534   PetscAssertPointer(celltypeLabel, 2);
5535   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5536   *celltypeLabel = dm->celltypeLabel;
5537   PetscFunctionReturn(PETSC_SUCCESS);
5538 }
5539 
5540 /*@
5541   DMPlexGetCellType - Get the polytope type of a given cell
5542 
5543   Not Collective
5544 
5545   Input Parameters:
5546 + dm   - The `DMPLEX` object
5547 - cell - The cell
5548 
5549   Output Parameter:
5550 . celltype - The polytope type of the cell
5551 
5552   Level: intermediate
5553 
5554 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeType`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5555 @*/
5556 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5557 {
5558   DM_Plex *mesh = (DM_Plex *)dm->data;
5559   DMLabel  label;
5560   PetscInt ct;
5561 
5562   PetscFunctionBegin;
5563   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5564   PetscAssertPointer(celltype, 3);
5565   if (mesh->tr) {
5566     PetscCall(DMPlexTransformGetCellType(mesh->tr, cell, celltype));
5567   } else {
5568     PetscInt pStart, pEnd;
5569 
5570     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, NULL));
5571     if (!mesh->cellTypes) { /* XXX remove? optimize? */
5572       PetscCall(PetscSectionGetChart(mesh->coneSection, NULL, &pEnd));
5573       PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5574       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5575       for (PetscInt p = pStart; p < pEnd; p++) {
5576         PetscCall(DMLabelGetValue(label, p, &ct));
5577         mesh->cellTypes[p - pStart].value_as_uint8 = (DMPolytopeType)ct;
5578       }
5579     }
5580     *celltype = (DMPolytopeType)mesh->cellTypes[cell - pStart].value_as_uint8;
5581     if (PetscDefined(USE_DEBUG)) {
5582       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5583       PetscCall(DMLabelGetValue(label, cell, &ct));
5584       PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5585       PetscCheck(ct == (PetscInt)*celltype, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid cellType for %" PetscInt_FMT ": %d != %" PetscInt_FMT, cell, (int)*celltype, ct);
5586     }
5587   }
5588   PetscFunctionReturn(PETSC_SUCCESS);
5589 }
5590 
5591 /*@
5592   DMPlexSetCellType - Set the polytope type of a given cell
5593 
5594   Not Collective
5595 
5596   Input Parameters:
5597 + dm       - The `DMPLEX` object
5598 . cell     - The cell
5599 - celltype - The polytope type of the cell
5600 
5601   Level: advanced
5602 
5603   Note:
5604   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5605   is executed. This function will override the computed type. However, if automatic classification will not succeed
5606   and a user wants to manually specify all types, the classification must be disabled by calling
5607   DMCreateLabel(dm, "celltype") before getting or setting any cell types.
5608 
5609 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5610 @*/
5611 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5612 {
5613   DM_Plex *mesh = (DM_Plex *)dm->data;
5614   DMLabel  label;
5615   PetscInt pStart, pEnd;
5616 
5617   PetscFunctionBegin;
5618   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5619   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
5620   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5621   PetscCall(DMLabelSetValue(label, cell, celltype));
5622   if (!mesh->cellTypes) PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5623   mesh->cellTypes[cell - pStart].value_as_uint8 = celltype;
5624   PetscFunctionReturn(PETSC_SUCCESS);
5625 }
5626 
5627 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5628 {
5629   PetscSection section;
5630   PetscInt     maxHeight;
5631   const char  *prefix;
5632 
5633   PetscFunctionBegin;
5634   PetscCall(DMClone(dm, cdm));
5635   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5636   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5637   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5638   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5639   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5640   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5641   PetscCall(DMSetLocalSection(*cdm, section));
5642   PetscCall(PetscSectionDestroy(&section));
5643 
5644   PetscCall(DMSetNumFields(*cdm, 1));
5645   PetscCall(DMCreateDS(*cdm));
5646   (*cdm)->cloneOpts = PETSC_TRUE;
5647   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5648   PetscFunctionReturn(PETSC_SUCCESS);
5649 }
5650 
5651 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5652 {
5653   Vec coordsLocal, cellCoordsLocal;
5654   DM  coordsDM, cellCoordsDM;
5655 
5656   PetscFunctionBegin;
5657   *field = NULL;
5658   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5659   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5660   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5661   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5662   if (coordsLocal && coordsDM) {
5663     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5664     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5665   }
5666   PetscFunctionReturn(PETSC_SUCCESS);
5667 }
5668 
5669 /*@
5670   DMPlexGetConeSection - Return a section which describes the layout of cone data
5671 
5672   Not Collective
5673 
5674   Input Parameter:
5675 . dm - The `DMPLEX` object
5676 
5677   Output Parameter:
5678 . section - The `PetscSection` object
5679 
5680   Level: developer
5681 
5682 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5683 @*/
5684 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5685 {
5686   DM_Plex *mesh = (DM_Plex *)dm->data;
5687 
5688   PetscFunctionBegin;
5689   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5690   if (section) *section = mesh->coneSection;
5691   PetscFunctionReturn(PETSC_SUCCESS);
5692 }
5693 
5694 /*@
5695   DMPlexGetSupportSection - Return a section which describes the layout of support data
5696 
5697   Not Collective
5698 
5699   Input Parameter:
5700 . dm - The `DMPLEX` object
5701 
5702   Output Parameter:
5703 . section - The `PetscSection` object
5704 
5705   Level: developer
5706 
5707 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5708 @*/
5709 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5710 {
5711   DM_Plex *mesh = (DM_Plex *)dm->data;
5712 
5713   PetscFunctionBegin;
5714   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5715   if (section) *section = mesh->supportSection;
5716   PetscFunctionReturn(PETSC_SUCCESS);
5717 }
5718 
5719 /*@C
5720   DMPlexGetCones - Return cone data
5721 
5722   Not Collective
5723 
5724   Input Parameter:
5725 . dm - The `DMPLEX` object
5726 
5727   Output Parameter:
5728 . cones - The cone for each point
5729 
5730   Level: developer
5731 
5732 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5733 @*/
5734 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5735 {
5736   DM_Plex *mesh = (DM_Plex *)dm->data;
5737 
5738   PetscFunctionBegin;
5739   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5740   if (cones) *cones = mesh->cones;
5741   PetscFunctionReturn(PETSC_SUCCESS);
5742 }
5743 
5744 /*@C
5745   DMPlexGetConeOrientations - Return cone orientation data
5746 
5747   Not Collective
5748 
5749   Input Parameter:
5750 . dm - The `DMPLEX` object
5751 
5752   Output Parameter:
5753 . coneOrientations - The array of cone orientations for all points
5754 
5755   Level: developer
5756 
5757   Notes:
5758   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points
5759   as returned by `DMPlexGetConeOrientation()`.
5760 
5761   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5762 
5763 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5764 @*/
5765 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5766 {
5767   DM_Plex *mesh = (DM_Plex *)dm->data;
5768 
5769   PetscFunctionBegin;
5770   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5771   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5772   PetscFunctionReturn(PETSC_SUCCESS);
5773 }
5774 
5775 /******************************** FEM Support **********************************/
5776 
5777 PetscErrorCode DMPlexGetAllCells_Internal(DM plex, IS *cellIS)
5778 {
5779   PetscInt depth;
5780 
5781   PetscFunctionBegin;
5782   PetscCall(DMPlexGetDepth(plex, &depth));
5783   PetscCall(DMGetStratumIS(plex, "dim", depth, cellIS));
5784   if (!*cellIS) PetscCall(DMGetStratumIS(plex, "depth", depth, cellIS));
5785   PetscFunctionReturn(PETSC_SUCCESS);
5786 }
5787 
5788 PetscErrorCode DMPlexGetAllFaces_Internal(DM plex, IS *faceIS)
5789 {
5790   PetscInt depth;
5791 
5792   PetscFunctionBegin;
5793   PetscCall(DMPlexGetDepth(plex, &depth));
5794   PetscCall(DMGetStratumIS(plex, "dim", depth - 1, faceIS));
5795   if (!*faceIS) PetscCall(DMGetStratumIS(plex, "depth", depth - 1, faceIS));
5796   PetscFunctionReturn(PETSC_SUCCESS);
5797 }
5798 
5799 /*
5800  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5801  representing a line in the section.
5802 */
5803 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(DM dm, PetscSection section, PetscInt field, PetscInt line, PetscInt *Nc, PetscInt *k, PetscBool *continuous, PetscBool *tensor)
5804 {
5805   PetscObject  obj;
5806   PetscClassId id;
5807   PetscFE      fe = NULL;
5808 
5809   PetscFunctionBeginHot;
5810   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5811   PetscCall(DMGetField(dm, field, NULL, &obj));
5812   PetscCall(PetscObjectGetClassId(obj, &id));
5813   if (id == PETSCFE_CLASSID) fe = (PetscFE)obj;
5814 
5815   if (!fe) {
5816     /* Assume the full interpolated mesh is in the chart; lines in particular */
5817     /* An order k SEM disc has k-1 dofs on an edge */
5818     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5819     *k = *k / *Nc + 1;
5820   } else {
5821     PetscInt       dual_space_size, dim;
5822     PetscDualSpace dsp;
5823 
5824     PetscCall(DMGetDimension(dm, &dim));
5825     PetscCall(PetscFEGetDualSpace(fe, &dsp));
5826     PetscCall(PetscDualSpaceGetDimension(dsp, &dual_space_size));
5827     *k = (PetscInt)PetscCeilReal(PetscPowReal(dual_space_size / *Nc, 1.0 / dim)) - 1;
5828     PetscCall(PetscDualSpaceLagrangeGetContinuity(dsp, continuous));
5829     PetscCall(PetscDualSpaceLagrangeGetTensor(dsp, tensor));
5830   }
5831   PetscFunctionReturn(PETSC_SUCCESS);
5832 }
5833 
5834 static PetscErrorCode GetFieldSize_Private(PetscInt dim, PetscInt k, PetscBool tensor, PetscInt *dof)
5835 {
5836   PetscFunctionBeginHot;
5837   if (tensor) {
5838     *dof = PetscPowInt(k + 1, dim);
5839   } else {
5840     switch (dim) {
5841     case 1:
5842       *dof = k + 1;
5843       break;
5844     case 2:
5845       *dof = ((k + 1) * (k + 2)) / 2;
5846       break;
5847     case 3:
5848       *dof = ((k + 1) * (k + 2) * (k + 3)) / 6;
5849       break;
5850     default:
5851       *dof = 0;
5852     }
5853   }
5854   PetscFunctionReturn(PETSC_SUCCESS);
5855 }
5856 
5857 /*@
5858   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5859   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5860   section provided (or the section of the `DM`).
5861 
5862   Input Parameters:
5863 + dm      - The `DM`
5864 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or `PETSC_DETERMINE`
5865 - section - The `PetscSection` to reorder, or `NULL` for the default section
5866 
5867   Example:
5868   A typical interpolated single-quad mesh might order points as
5869 .vb
5870   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5871 
5872   v4 -- e6 -- v3
5873   |           |
5874   e7    c0    e8
5875   |           |
5876   v1 -- e5 -- v2
5877 .ve
5878 
5879   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5880   dofs in the order of points, e.g.,
5881 .vb
5882     c0 -> [0,1,2,3]
5883     v1 -> [4]
5884     ...
5885     e5 -> [8, 9]
5886 .ve
5887 
5888   which corresponds to the dofs
5889 .vb
5890     6   10  11  7
5891     13  2   3   15
5892     12  0   1   14
5893     4   8   9   5
5894 .ve
5895 
5896   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5897 .vb
5898   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5899 .ve
5900 
5901   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5902 .vb
5903    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5904 .ve
5905 
5906   Level: developer
5907 
5908   Notes:
5909   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5910   degree of the basis.
5911 
5912   This is required to run with libCEED.
5913 
5914 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5915 @*/
5916 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5917 {
5918   DMLabel   label;
5919   PetscInt  dim, depth = -1, eStart = -1, Nf;
5920   PetscBool continuous = PETSC_TRUE, tensor = PETSC_TRUE;
5921 
5922   PetscFunctionBegin;
5923   PetscCall(DMGetDimension(dm, &dim));
5924   if (dim < 1) PetscFunctionReturn(PETSC_SUCCESS);
5925   if (point < 0) {
5926     PetscInt sStart, sEnd;
5927 
5928     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5929     point = sEnd - sStart ? sStart : point;
5930   }
5931   PetscCall(DMPlexGetDepthLabel(dm, &label));
5932   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5933   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5934   if (depth == 1) {
5935     eStart = point;
5936   } else if (depth == dim) {
5937     const PetscInt *cone;
5938 
5939     PetscCall(DMPlexGetCone(dm, point, &cone));
5940     if (dim == 2) eStart = cone[0];
5941     else if (dim == 3) {
5942       const PetscInt *cone2;
5943       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5944       eStart = cone2[0];
5945     } 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);
5946   } 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);
5947 
5948   PetscCall(PetscSectionGetNumFields(section, &Nf));
5949   for (PetscInt d = 1; d <= dim; d++) {
5950     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5951     PetscInt *perm;
5952 
5953     for (f = 0; f < Nf; ++f) {
5954       PetscInt dof;
5955 
5956       PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5957       PetscCheck(dim == 1 || tensor || !continuous, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Continuous field %" PetscInt_FMT " must have a tensor product discretization", f);
5958       if (!continuous && d < dim) continue;
5959       PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
5960       size += dof * Nc;
5961     }
5962     PetscCall(PetscMalloc1(size, &perm));
5963     for (f = 0; f < Nf; ++f) {
5964       switch (d) {
5965       case 1:
5966         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5967         if (!continuous && d < dim) continue;
5968         /*
5969          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5970          We want              [ vtx0; edge of length k-1; vtx1 ]
5971          */
5972         if (continuous) {
5973           for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
5974           for (i = 0; i < k - 1; i++)
5975             for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
5976           for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
5977           foffset = offset;
5978         } else {
5979           PetscInt dof;
5980 
5981           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
5982           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
5983           foffset = offset;
5984         }
5985         break;
5986       case 2:
5987         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5988         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5989         if (!continuous && d < dim) continue;
5990         /* The SEM order is
5991 
5992          v_lb, {e_b}, v_rb,
5993          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5994          v_lt, reverse {e_t}, v_rt
5995          */
5996         if (continuous) {
5997           const PetscInt of   = 0;
5998           const PetscInt oeb  = of + PetscSqr(k - 1);
5999           const PetscInt oer  = oeb + (k - 1);
6000           const PetscInt oet  = oer + (k - 1);
6001           const PetscInt oel  = oet + (k - 1);
6002           const PetscInt ovlb = oel + (k - 1);
6003           const PetscInt ovrb = ovlb + 1;
6004           const PetscInt ovrt = ovrb + 1;
6005           const PetscInt ovlt = ovrt + 1;
6006           PetscInt       o;
6007 
6008           /* bottom */
6009           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
6010           for (o = oeb; o < oer; ++o)
6011             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6012           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
6013           /* middle */
6014           for (i = 0; i < k - 1; ++i) {
6015             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
6016             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
6017               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6018             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
6019           }
6020           /* top */
6021           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
6022           for (o = oel - 1; o >= oet; --o)
6023             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6024           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
6025           foffset = offset;
6026         } else {
6027           PetscInt dof;
6028 
6029           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6030           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6031           foffset = offset;
6032         }
6033         break;
6034       case 3:
6035         /* The original hex closure is
6036 
6037          {c,
6038          f_b, f_t, f_f, f_b, f_r, f_l,
6039          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
6040          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
6041          */
6042         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6043         if (!continuous && d < dim) continue;
6044         /* The SEM order is
6045          Bottom Slice
6046          v_blf, {e^{(k-1)-n}_bf}, v_brf,
6047          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
6048          v_blb, {e_bb}, v_brb,
6049 
6050          Middle Slice (j)
6051          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
6052          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
6053          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
6054 
6055          Top Slice
6056          v_tlf, {e_tf}, v_trf,
6057          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
6058          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
6059          */
6060         if (continuous) {
6061           const PetscInt oc    = 0;
6062           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
6063           const PetscInt oft   = ofb + PetscSqr(k - 1);
6064           const PetscInt off   = oft + PetscSqr(k - 1);
6065           const PetscInt ofk   = off + PetscSqr(k - 1);
6066           const PetscInt ofr   = ofk + PetscSqr(k - 1);
6067           const PetscInt ofl   = ofr + PetscSqr(k - 1);
6068           const PetscInt oebl  = ofl + PetscSqr(k - 1);
6069           const PetscInt oebb  = oebl + (k - 1);
6070           const PetscInt oebr  = oebb + (k - 1);
6071           const PetscInt oebf  = oebr + (k - 1);
6072           const PetscInt oetf  = oebf + (k - 1);
6073           const PetscInt oetr  = oetf + (k - 1);
6074           const PetscInt oetb  = oetr + (k - 1);
6075           const PetscInt oetl  = oetb + (k - 1);
6076           const PetscInt oerf  = oetl + (k - 1);
6077           const PetscInt oelf  = oerf + (k - 1);
6078           const PetscInt oelb  = oelf + (k - 1);
6079           const PetscInt oerb  = oelb + (k - 1);
6080           const PetscInt ovblf = oerb + (k - 1);
6081           const PetscInt ovblb = ovblf + 1;
6082           const PetscInt ovbrb = ovblb + 1;
6083           const PetscInt ovbrf = ovbrb + 1;
6084           const PetscInt ovtlf = ovbrf + 1;
6085           const PetscInt ovtrf = ovtlf + 1;
6086           const PetscInt ovtrb = ovtrf + 1;
6087           const PetscInt ovtlb = ovtrb + 1;
6088           PetscInt       o, n;
6089 
6090           /* Bottom Slice */
6091           /*   bottom */
6092           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
6093           for (o = oetf - 1; o >= oebf; --o)
6094             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6095           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
6096           /*   middle */
6097           for (i = 0; i < k - 1; ++i) {
6098             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
6099             for (n = 0; n < k - 1; ++n) {
6100               o = ofb + n * (k - 1) + i;
6101               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6102             }
6103             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
6104           }
6105           /*   top */
6106           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
6107           for (o = oebb; o < oebr; ++o)
6108             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6109           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
6110 
6111           /* Middle Slice */
6112           for (j = 0; j < k - 1; ++j) {
6113             /*   bottom */
6114             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
6115             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
6116               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6117             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
6118             /*   middle */
6119             for (i = 0; i < k - 1; ++i) {
6120               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
6121               for (n = 0; n < k - 1; ++n)
6122                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
6123               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
6124             }
6125             /*   top */
6126             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
6127             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
6128               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6129             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
6130           }
6131 
6132           /* Top Slice */
6133           /*   bottom */
6134           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
6135           for (o = oetf; o < oetr; ++o)
6136             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6137           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
6138           /*   middle */
6139           for (i = 0; i < k - 1; ++i) {
6140             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
6141             for (n = 0; n < k - 1; ++n)
6142               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
6143             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
6144           }
6145           /*   top */
6146           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
6147           for (o = oetl - 1; o >= oetb; --o)
6148             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6149           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
6150 
6151           foffset = offset;
6152         } else {
6153           PetscInt dof;
6154 
6155           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6156           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6157           foffset = offset;
6158         }
6159         break;
6160       default:
6161         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
6162       }
6163     }
6164     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
6165     /* Check permutation */
6166     {
6167       PetscInt *check;
6168 
6169       PetscCall(PetscMalloc1(size, &check));
6170       for (i = 0; i < size; ++i) {
6171         check[i] = -1;
6172         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
6173       }
6174       for (i = 0; i < size; ++i) check[perm[i]] = i;
6175       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
6176       PetscCall(PetscFree(check));
6177     }
6178     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
6179     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
6180       PetscInt *loc_perm;
6181       PetscCall(PetscMalloc1(size * 2, &loc_perm));
6182       for (PetscInt i = 0; i < size; i++) {
6183         loc_perm[i]        = perm[i];
6184         loc_perm[size + i] = size + perm[i];
6185       }
6186       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
6187     }
6188   }
6189   PetscFunctionReturn(PETSC_SUCCESS);
6190 }
6191 
6192 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
6193 {
6194   PetscDS  prob;
6195   PetscInt depth, Nf, h;
6196   DMLabel  label;
6197 
6198   PetscFunctionBeginHot;
6199   PetscCall(DMGetDS(dm, &prob));
6200   Nf      = prob->Nf;
6201   label   = dm->depthLabel;
6202   *dspace = NULL;
6203   if (field < Nf) {
6204     PetscObject disc = prob->disc[field];
6205 
6206     if (disc->classid == PETSCFE_CLASSID) {
6207       PetscDualSpace dsp;
6208 
6209       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
6210       PetscCall(DMLabelGetNumValues(label, &depth));
6211       PetscCall(DMLabelGetValue(label, point, &h));
6212       h = depth - 1 - h;
6213       if (h) {
6214         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
6215       } else {
6216         *dspace = dsp;
6217       }
6218     }
6219   }
6220   PetscFunctionReturn(PETSC_SUCCESS);
6221 }
6222 
6223 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6224 {
6225   PetscScalar       *array;
6226   const PetscScalar *vArray;
6227   const PetscInt    *cone, *coneO;
6228   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
6229 
6230   PetscFunctionBeginHot;
6231   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6232   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6233   PetscCall(DMPlexGetCone(dm, point, &cone));
6234   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6235   if (!values || !*values) {
6236     if ((point >= pStart) && (point < pEnd)) {
6237       PetscInt dof;
6238 
6239       PetscCall(PetscSectionGetDof(section, point, &dof));
6240       size += dof;
6241     }
6242     for (p = 0; p < numPoints; ++p) {
6243       const PetscInt cp = cone[p];
6244       PetscInt       dof;
6245 
6246       if ((cp < pStart) || (cp >= pEnd)) continue;
6247       PetscCall(PetscSectionGetDof(section, cp, &dof));
6248       size += dof;
6249     }
6250     if (!values) {
6251       if (csize) *csize = size;
6252       PetscFunctionReturn(PETSC_SUCCESS);
6253     }
6254     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
6255   } else {
6256     array = *values;
6257   }
6258   size = 0;
6259   PetscCall(VecGetArrayRead(v, &vArray));
6260   if ((point >= pStart) && (point < pEnd)) {
6261     PetscInt           dof, off, d;
6262     const PetscScalar *varr;
6263 
6264     PetscCall(PetscSectionGetDof(section, point, &dof));
6265     PetscCall(PetscSectionGetOffset(section, point, &off));
6266     varr = PetscSafePointerPlusOffset(vArray, off);
6267     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6268     size += dof;
6269   }
6270   for (p = 0; p < numPoints; ++p) {
6271     const PetscInt     cp = cone[p];
6272     PetscInt           o  = coneO[p];
6273     PetscInt           dof, off, d;
6274     const PetscScalar *varr;
6275 
6276     if ((cp < pStart) || (cp >= pEnd)) continue;
6277     PetscCall(PetscSectionGetDof(section, cp, &dof));
6278     PetscCall(PetscSectionGetOffset(section, cp, &off));
6279     varr = PetscSafePointerPlusOffset(vArray, off);
6280     if (o >= 0) {
6281       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6282     } else {
6283       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
6284     }
6285     size += dof;
6286   }
6287   PetscCall(VecRestoreArrayRead(v, &vArray));
6288   if (!*values) {
6289     if (csize) *csize = size;
6290     *values = array;
6291   } else {
6292     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6293     *csize = size;
6294   }
6295   PetscFunctionReturn(PETSC_SUCCESS);
6296 }
6297 
6298 /* Compress out points not in the section */
6299 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
6300 {
6301   const PetscInt np = *numPoints;
6302   PetscInt       pStart, pEnd, p, q;
6303 
6304   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6305   for (p = 0, q = 0; p < np; ++p) {
6306     const PetscInt r = points[p * 2];
6307     if ((r >= pStart) && (r < pEnd)) {
6308       points[q * 2]     = r;
6309       points[q * 2 + 1] = points[p * 2 + 1];
6310       ++q;
6311     }
6312   }
6313   *numPoints = q;
6314   return PETSC_SUCCESS;
6315 }
6316 
6317 /* Compressed closure does not apply closure permutation */
6318 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt ornt, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6319 {
6320   const PetscInt *cla = NULL;
6321   PetscInt        np, *pts = NULL;
6322 
6323   PetscFunctionBeginHot;
6324   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
6325   if (!ornt && *clPoints) {
6326     PetscInt dof, off;
6327 
6328     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
6329     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
6330     PetscCall(ISGetIndices(*clPoints, &cla));
6331     np  = dof / 2;
6332     pts = PetscSafePointerPlusOffset((PetscInt *)cla, off);
6333   } else {
6334     PetscCall(DMPlexGetTransitiveClosure_Internal(dm, point, ornt, PETSC_TRUE, &np, &pts));
6335     PetscCall(CompressPoints_Private(section, &np, pts));
6336   }
6337   *numPoints = np;
6338   *points    = pts;
6339   *clp       = cla;
6340   PetscFunctionReturn(PETSC_SUCCESS);
6341 }
6342 
6343 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6344 {
6345   PetscFunctionBeginHot;
6346   if (!*clPoints) {
6347     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
6348   } else {
6349     PetscCall(ISRestoreIndices(*clPoints, clp));
6350   }
6351   *numPoints = 0;
6352   *points    = NULL;
6353   *clSec     = NULL;
6354   *clPoints  = NULL;
6355   *clp       = NULL;
6356   PetscFunctionReturn(PETSC_SUCCESS);
6357 }
6358 
6359 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
6360 {
6361   PetscInt            offset = 0, p;
6362   const PetscInt    **perms  = NULL;
6363   const PetscScalar **flips  = NULL;
6364 
6365   PetscFunctionBeginHot;
6366   *size = 0;
6367   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6368   for (p = 0; p < numPoints; p++) {
6369     const PetscInt     point = points[2 * p];
6370     const PetscInt    *perm  = perms ? perms[p] : NULL;
6371     const PetscScalar *flip  = flips ? flips[p] : NULL;
6372     PetscInt           dof, off, d;
6373     const PetscScalar *varr;
6374 
6375     PetscCall(PetscSectionGetDof(section, point, &dof));
6376     PetscCall(PetscSectionGetOffset(section, point, &off));
6377     varr = PetscSafePointerPlusOffset(vArray, off);
6378     if (clperm) {
6379       if (perm) {
6380         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
6381       } else {
6382         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
6383       }
6384       if (flip) {
6385         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
6386       }
6387     } else {
6388       if (perm) {
6389         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
6390       } else {
6391         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
6392       }
6393       if (flip) {
6394         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
6395       }
6396     }
6397     offset += dof;
6398   }
6399   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6400   *size = offset;
6401   PetscFunctionReturn(PETSC_SUCCESS);
6402 }
6403 
6404 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[])
6405 {
6406   PetscInt offset = 0, f;
6407 
6408   PetscFunctionBeginHot;
6409   *size = 0;
6410   for (f = 0; f < numFields; ++f) {
6411     PetscInt            p;
6412     const PetscInt    **perms = NULL;
6413     const PetscScalar **flips = NULL;
6414 
6415     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6416     for (p = 0; p < numPoints; p++) {
6417       const PetscInt     point = points[2 * p];
6418       PetscInt           fdof, foff, b;
6419       const PetscScalar *varr;
6420       const PetscInt    *perm = perms ? perms[p] : NULL;
6421       const PetscScalar *flip = flips ? flips[p] : NULL;
6422 
6423       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6424       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6425       varr = &vArray[foff];
6426       if (clperm) {
6427         if (perm) {
6428           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
6429         } else {
6430           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
6431         }
6432         if (flip) {
6433           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
6434         }
6435       } else {
6436         if (perm) {
6437           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
6438         } else {
6439           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
6440         }
6441         if (flip) {
6442           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
6443         }
6444       }
6445       offset += fdof;
6446     }
6447     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6448   }
6449   *size = offset;
6450   PetscFunctionReturn(PETSC_SUCCESS);
6451 }
6452 
6453 PetscErrorCode DMPlexVecGetOrientedClosure_Internal(DM dm, PetscSection section, PetscBool useClPerm, Vec v, PetscInt point, PetscInt ornt, PetscInt *csize, PetscScalar *values[])
6454 {
6455   PetscSection    clSection;
6456   IS              clPoints;
6457   PetscInt       *points = NULL;
6458   const PetscInt *clp, *perm = NULL;
6459   PetscInt        depth, numFields, numPoints, asize;
6460 
6461   PetscFunctionBeginHot;
6462   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6463   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6464   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6465   PetscValidHeaderSpecific(v, VEC_CLASSID, 4);
6466   PetscCall(DMPlexGetDepth(dm, &depth));
6467   PetscCall(PetscSectionGetNumFields(section, &numFields));
6468   if (depth == 1 && numFields < 2) {
6469     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6470     PetscFunctionReturn(PETSC_SUCCESS);
6471   }
6472   /* Get points */
6473   PetscCall(DMPlexGetCompressedClosure(dm, section, point, ornt, &numPoints, &points, &clSection, &clPoints, &clp));
6474   /* Get sizes */
6475   asize = 0;
6476   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6477     PetscInt dof;
6478     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6479     asize += dof;
6480   }
6481   if (values) {
6482     const PetscScalar *vArray;
6483     PetscInt           size;
6484 
6485     if (*values) {
6486       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);
6487     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6488     if (useClPerm) PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6489     PetscCall(VecGetArrayRead(v, &vArray));
6490     /* Get values */
6491     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6492     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6493     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6494     /* Cleanup array */
6495     PetscCall(VecRestoreArrayRead(v, &vArray));
6496   }
6497   if (csize) *csize = asize;
6498   /* Cleanup points */
6499   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6500   PetscFunctionReturn(PETSC_SUCCESS);
6501 }
6502 
6503 /*@C
6504   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
6505 
6506   Not collective
6507 
6508   Input Parameters:
6509 + dm      - The `DM`
6510 . section - The section describing the layout in `v`, or `NULL` to use the default section
6511 . v       - The local vector
6512 - point   - The point in the `DM`
6513 
6514   Input/Output Parameters:
6515 + csize  - The size of the input values array, or `NULL`; on output the number of values in the closure
6516 - values - An array to use for the values, or *values = `NULL` to have it allocated automatically;
6517            if the user provided `NULL`, it is a borrowed array and should not be freed, use  `DMPlexVecRestoreClosure()` to return it
6518 
6519   Level: intermediate
6520 
6521   Notes:
6522   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6523   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
6524   assembly function, and a user may already have allocated storage for this operation.
6525 
6526   A typical use could be
6527 .vb
6528    values = NULL;
6529    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6530    for (cl = 0; cl < clSize; ++cl) {
6531      <Compute on closure>
6532    }
6533    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
6534 .ve
6535   or
6536 .vb
6537    PetscMalloc1(clMaxSize, &values);
6538    for (p = pStart; p < pEnd; ++p) {
6539      clSize = clMaxSize;
6540      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6541      for (cl = 0; cl < clSize; ++cl) {
6542        <Compute on closure>
6543      }
6544    }
6545    PetscFree(values);
6546 .ve
6547 
6548   Fortran Notes:
6549   The `csize` argument is not present in the Fortran binding.
6550 
6551   `values` must be declared with
6552 .vb
6553   PetscScalar,dimension(:),pointer   :: values
6554 .ve
6555   and it will be allocated internally by PETSc to hold the values returned
6556 
6557 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6558 @*/
6559 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6560 {
6561   PetscFunctionBeginHot;
6562   PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, section, PETSC_TRUE, v, point, 0, csize, values));
6563   PetscFunctionReturn(PETSC_SUCCESS);
6564 }
6565 
6566 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6567 {
6568   DMLabel            depthLabel;
6569   PetscSection       clSection;
6570   IS                 clPoints;
6571   PetscScalar       *array;
6572   const PetscScalar *vArray;
6573   PetscInt          *points = NULL;
6574   const PetscInt    *clp, *perm = NULL;
6575   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6576 
6577   PetscFunctionBeginHot;
6578   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6579   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6580   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6581   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6582   PetscCall(DMPlexGetDepth(dm, &mdepth));
6583   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6584   PetscCall(PetscSectionGetNumFields(section, &numFields));
6585   if (mdepth == 1 && numFields < 2) {
6586     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6587     PetscFunctionReturn(PETSC_SUCCESS);
6588   }
6589   /* Get points */
6590   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6591   for (clsize = 0, p = 0; p < Np; p++) {
6592     PetscInt dof;
6593     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6594     clsize += dof;
6595   }
6596   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6597   /* Filter points */
6598   for (p = 0; p < numPoints * 2; p += 2) {
6599     PetscInt dep;
6600 
6601     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6602     if (dep != depth) continue;
6603     points[Np * 2 + 0] = points[p];
6604     points[Np * 2 + 1] = points[p + 1];
6605     ++Np;
6606   }
6607   /* Get array */
6608   if (!values || !*values) {
6609     PetscInt asize = 0, dof;
6610 
6611     for (p = 0; p < Np * 2; p += 2) {
6612       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6613       asize += dof;
6614     }
6615     if (!values) {
6616       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6617       if (csize) *csize = asize;
6618       PetscFunctionReturn(PETSC_SUCCESS);
6619     }
6620     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6621   } else {
6622     array = *values;
6623   }
6624   PetscCall(VecGetArrayRead(v, &vArray));
6625   /* Get values */
6626   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6627   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6628   /* Cleanup points */
6629   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6630   /* Cleanup array */
6631   PetscCall(VecRestoreArrayRead(v, &vArray));
6632   if (!*values) {
6633     if (csize) *csize = size;
6634     *values = array;
6635   } else {
6636     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6637     *csize = size;
6638   }
6639   PetscFunctionReturn(PETSC_SUCCESS);
6640 }
6641 
6642 /*@C
6643   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point' obtained with `DMPlexVecGetClosure()`
6644 
6645   Not collective
6646 
6647   Input Parameters:
6648 + dm      - The `DM`
6649 . section - The section describing the layout in `v`, or `NULL` to use the default section
6650 . v       - The local vector
6651 . point   - The point in the `DM`
6652 . csize   - The number of values in the closure, or `NULL`
6653 - values  - The array of values
6654 
6655   Level: intermediate
6656 
6657   Note:
6658   The array values are discarded and not copied back into `v`. In order to copy values back to `v`, use `DMPlexVecSetClosure()`
6659 
6660   Fortran Note:
6661   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6662 
6663 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6664 @*/
6665 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6666 {
6667   PetscInt size = 0;
6668 
6669   PetscFunctionBegin;
6670   /* Should work without recalculating size */
6671   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6672   *values = NULL;
6673   PetscFunctionReturn(PETSC_SUCCESS);
6674 }
6675 
6676 static inline void add(PetscScalar *x, PetscScalar y)
6677 {
6678   *x += y;
6679 }
6680 static inline void insert(PetscScalar *x, PetscScalar y)
6681 {
6682   *x = y;
6683 }
6684 
6685 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[])
6686 {
6687   PetscInt        cdof;  /* The number of constraints on this point */
6688   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6689   PetscScalar    *a;
6690   PetscInt        off, cind = 0, k;
6691 
6692   PetscFunctionBegin;
6693   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6694   PetscCall(PetscSectionGetOffset(section, point, &off));
6695   a = &array[off];
6696   if (!cdof || setBC) {
6697     if (clperm) {
6698       if (perm) {
6699         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6700       } else {
6701         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6702       }
6703     } else {
6704       if (perm) {
6705         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6706       } else {
6707         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6708       }
6709     }
6710   } else {
6711     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6712     if (clperm) {
6713       if (perm) {
6714         for (k = 0; k < dof; ++k) {
6715           if ((cind < cdof) && (k == cdofs[cind])) {
6716             ++cind;
6717             continue;
6718           }
6719           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6720         }
6721       } else {
6722         for (k = 0; k < dof; ++k) {
6723           if ((cind < cdof) && (k == cdofs[cind])) {
6724             ++cind;
6725             continue;
6726           }
6727           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6728         }
6729       }
6730     } else {
6731       if (perm) {
6732         for (k = 0; k < dof; ++k) {
6733           if ((cind < cdof) && (k == cdofs[cind])) {
6734             ++cind;
6735             continue;
6736           }
6737           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6738         }
6739       } else {
6740         for (k = 0; k < dof; ++k) {
6741           if ((cind < cdof) && (k == cdofs[cind])) {
6742             ++cind;
6743             continue;
6744           }
6745           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6746         }
6747       }
6748     }
6749   }
6750   PetscFunctionReturn(PETSC_SUCCESS);
6751 }
6752 
6753 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[])
6754 {
6755   PetscInt        cdof;  /* The number of constraints on this point */
6756   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6757   PetscScalar    *a;
6758   PetscInt        off, cind = 0, k;
6759 
6760   PetscFunctionBegin;
6761   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6762   PetscCall(PetscSectionGetOffset(section, point, &off));
6763   a = &array[off];
6764   if (cdof) {
6765     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6766     if (clperm) {
6767       if (perm) {
6768         for (k = 0; k < dof; ++k) {
6769           if ((cind < cdof) && (k == cdofs[cind])) {
6770             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6771             cind++;
6772           }
6773         }
6774       } else {
6775         for (k = 0; k < dof; ++k) {
6776           if ((cind < cdof) && (k == cdofs[cind])) {
6777             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6778             cind++;
6779           }
6780         }
6781       }
6782     } else {
6783       if (perm) {
6784         for (k = 0; k < dof; ++k) {
6785           if ((cind < cdof) && (k == cdofs[cind])) {
6786             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6787             cind++;
6788           }
6789         }
6790       } else {
6791         for (k = 0; k < dof; ++k) {
6792           if ((cind < cdof) && (k == cdofs[cind])) {
6793             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6794             cind++;
6795           }
6796         }
6797       }
6798     }
6799   }
6800   PetscFunctionReturn(PETSC_SUCCESS);
6801 }
6802 
6803 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[])
6804 {
6805   PetscScalar    *a;
6806   PetscInt        fdof, foff, fcdof, foffset = *offset;
6807   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6808   PetscInt        cind = 0, b;
6809 
6810   PetscFunctionBegin;
6811   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6812   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6813   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6814   a = &array[foff];
6815   if (!fcdof || setBC) {
6816     if (clperm) {
6817       if (perm) {
6818         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6819       } else {
6820         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6821       }
6822     } else {
6823       if (perm) {
6824         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6825       } else {
6826         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6827       }
6828     }
6829   } else {
6830     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6831     if (clperm) {
6832       if (perm) {
6833         for (b = 0; b < fdof; b++) {
6834           if ((cind < fcdof) && (b == fcdofs[cind])) {
6835             ++cind;
6836             continue;
6837           }
6838           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6839         }
6840       } else {
6841         for (b = 0; b < fdof; b++) {
6842           if ((cind < fcdof) && (b == fcdofs[cind])) {
6843             ++cind;
6844             continue;
6845           }
6846           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6847         }
6848       }
6849     } else {
6850       if (perm) {
6851         for (b = 0; b < fdof; b++) {
6852           if ((cind < fcdof) && (b == fcdofs[cind])) {
6853             ++cind;
6854             continue;
6855           }
6856           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6857         }
6858       } else {
6859         for (b = 0; b < fdof; b++) {
6860           if ((cind < fcdof) && (b == fcdofs[cind])) {
6861             ++cind;
6862             continue;
6863           }
6864           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6865         }
6866       }
6867     }
6868   }
6869   *offset += fdof;
6870   PetscFunctionReturn(PETSC_SUCCESS);
6871 }
6872 
6873 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[])
6874 {
6875   PetscScalar    *a;
6876   PetscInt        fdof, foff, fcdof, foffset = *offset;
6877   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6878   PetscInt        Nc, cind = 0, ncind = 0, b;
6879   PetscBool       ncSet, fcSet;
6880 
6881   PetscFunctionBegin;
6882   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6883   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6884   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6885   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6886   a = &array[foff];
6887   if (fcdof) {
6888     /* We just override fcdof and fcdofs with Ncc and comps */
6889     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6890     if (clperm) {
6891       if (perm) {
6892         if (comps) {
6893           for (b = 0; b < fdof; b++) {
6894             ncSet = fcSet = PETSC_FALSE;
6895             if (b % Nc == comps[ncind]) {
6896               ncind = (ncind + 1) % Ncc;
6897               ncSet = PETSC_TRUE;
6898             }
6899             if ((cind < fcdof) && (b == fcdofs[cind])) {
6900               ++cind;
6901               fcSet = PETSC_TRUE;
6902             }
6903             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6904           }
6905         } else {
6906           for (b = 0; b < fdof; b++) {
6907             if ((cind < fcdof) && (b == fcdofs[cind])) {
6908               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6909               ++cind;
6910             }
6911           }
6912         }
6913       } else {
6914         if (comps) {
6915           for (b = 0; b < fdof; b++) {
6916             ncSet = fcSet = PETSC_FALSE;
6917             if (b % Nc == comps[ncind]) {
6918               ncind = (ncind + 1) % Ncc;
6919               ncSet = PETSC_TRUE;
6920             }
6921             if ((cind < fcdof) && (b == fcdofs[cind])) {
6922               ++cind;
6923               fcSet = PETSC_TRUE;
6924             }
6925             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6926           }
6927         } else {
6928           for (b = 0; b < fdof; b++) {
6929             if ((cind < fcdof) && (b == fcdofs[cind])) {
6930               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6931               ++cind;
6932             }
6933           }
6934         }
6935       }
6936     } else {
6937       if (perm) {
6938         if (comps) {
6939           for (b = 0; b < fdof; b++) {
6940             ncSet = fcSet = PETSC_FALSE;
6941             if (b % Nc == comps[ncind]) {
6942               ncind = (ncind + 1) % Ncc;
6943               ncSet = PETSC_TRUE;
6944             }
6945             if ((cind < fcdof) && (b == fcdofs[cind])) {
6946               ++cind;
6947               fcSet = PETSC_TRUE;
6948             }
6949             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6950           }
6951         } else {
6952           for (b = 0; b < fdof; b++) {
6953             if ((cind < fcdof) && (b == fcdofs[cind])) {
6954               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6955               ++cind;
6956             }
6957           }
6958         }
6959       } else {
6960         if (comps) {
6961           for (b = 0; b < fdof; b++) {
6962             ncSet = fcSet = PETSC_FALSE;
6963             if (b % Nc == comps[ncind]) {
6964               ncind = (ncind + 1) % Ncc;
6965               ncSet = PETSC_TRUE;
6966             }
6967             if ((cind < fcdof) && (b == fcdofs[cind])) {
6968               ++cind;
6969               fcSet = PETSC_TRUE;
6970             }
6971             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6972           }
6973         } else {
6974           for (b = 0; b < fdof; b++) {
6975             if ((cind < fcdof) && (b == fcdofs[cind])) {
6976               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6977               ++cind;
6978             }
6979           }
6980         }
6981       }
6982     }
6983   }
6984   *offset += fdof;
6985   PetscFunctionReturn(PETSC_SUCCESS);
6986 }
6987 
6988 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6989 {
6990   PetscScalar    *array;
6991   const PetscInt *cone, *coneO;
6992   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6993 
6994   PetscFunctionBeginHot;
6995   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6996   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6997   PetscCall(DMPlexGetCone(dm, point, &cone));
6998   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6999   PetscCall(VecGetArray(v, &array));
7000   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
7001     const PetscInt cp = !p ? point : cone[p - 1];
7002     const PetscInt o  = !p ? 0 : coneO[p - 1];
7003 
7004     if ((cp < pStart) || (cp >= pEnd)) {
7005       dof = 0;
7006       continue;
7007     }
7008     PetscCall(PetscSectionGetDof(section, cp, &dof));
7009     /* ADD_VALUES */
7010     {
7011       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7012       PetscScalar    *a;
7013       PetscInt        cdof, coff, cind = 0, k;
7014 
7015       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
7016       PetscCall(PetscSectionGetOffset(section, cp, &coff));
7017       a = &array[coff];
7018       if (!cdof) {
7019         if (o >= 0) {
7020           for (k = 0; k < dof; ++k) a[k] += values[off + k];
7021         } else {
7022           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
7023         }
7024       } else {
7025         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
7026         if (o >= 0) {
7027           for (k = 0; k < dof; ++k) {
7028             if ((cind < cdof) && (k == cdofs[cind])) {
7029               ++cind;
7030               continue;
7031             }
7032             a[k] += values[off + k];
7033           }
7034         } else {
7035           for (k = 0; k < dof; ++k) {
7036             if ((cind < cdof) && (k == cdofs[cind])) {
7037               ++cind;
7038               continue;
7039             }
7040             a[k] += values[off + dof - k - 1];
7041           }
7042         }
7043       }
7044     }
7045   }
7046   PetscCall(VecRestoreArray(v, &array));
7047   PetscFunctionReturn(PETSC_SUCCESS);
7048 }
7049 
7050 /*@C
7051   DMPlexVecSetClosure - Set an array of the values on the closure of `point`
7052 
7053   Not collective
7054 
7055   Input Parameters:
7056 + dm      - The `DM`
7057 . section - The section describing the layout in `v`, or `NULL` to use the default section
7058 . v       - The local vector
7059 . point   - The point in the `DM`
7060 . values  - The array of values
7061 - mode    - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
7062             where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
7063 
7064   Level: intermediate
7065 
7066   Note:
7067   Usually the input arrays were obtained with `DMPlexVecGetClosure()`
7068 
7069   Fortran Note:
7070   `values` must be declared with
7071 .vb
7072   PetscScalar,dimension(:),pointer   :: values
7073 .ve
7074 
7075 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
7076 @*/
7077 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
7078 {
7079   PetscSection    clSection;
7080   IS              clPoints;
7081   PetscScalar    *array;
7082   PetscInt       *points = NULL;
7083   const PetscInt *clp, *clperm = NULL;
7084   PetscInt        depth, numFields, numPoints, p, clsize;
7085 
7086   PetscFunctionBeginHot;
7087   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7088   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7089   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7090   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7091   PetscCall(DMPlexGetDepth(dm, &depth));
7092   PetscCall(PetscSectionGetNumFields(section, &numFields));
7093   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
7094     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
7095     PetscFunctionReturn(PETSC_SUCCESS);
7096   }
7097   /* Get points */
7098   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7099   for (clsize = 0, p = 0; p < numPoints; p++) {
7100     PetscInt dof;
7101     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7102     clsize += dof;
7103   }
7104   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7105   /* Get array */
7106   PetscCall(VecGetArray(v, &array));
7107   /* Get values */
7108   if (numFields > 0) {
7109     PetscInt offset = 0, f;
7110     for (f = 0; f < numFields; ++f) {
7111       const PetscInt    **perms = NULL;
7112       const PetscScalar **flips = NULL;
7113 
7114       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7115       switch (mode) {
7116       case INSERT_VALUES:
7117         for (p = 0; p < numPoints; p++) {
7118           const PetscInt     point = points[2 * p];
7119           const PetscInt    *perm  = perms ? perms[p] : NULL;
7120           const PetscScalar *flip  = flips ? flips[p] : NULL;
7121           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array));
7122         }
7123         break;
7124       case INSERT_ALL_VALUES:
7125         for (p = 0; p < numPoints; p++) {
7126           const PetscInt     point = points[2 * p];
7127           const PetscInt    *perm  = perms ? perms[p] : NULL;
7128           const PetscScalar *flip  = flips ? flips[p] : NULL;
7129           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array));
7130         }
7131         break;
7132       case INSERT_BC_VALUES:
7133         for (p = 0; p < numPoints; p++) {
7134           const PetscInt     point = points[2 * p];
7135           const PetscInt    *perm  = perms ? perms[p] : NULL;
7136           const PetscScalar *flip  = flips ? flips[p] : NULL;
7137           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array));
7138         }
7139         break;
7140       case ADD_VALUES:
7141         for (p = 0; p < numPoints; p++) {
7142           const PetscInt     point = points[2 * p];
7143           const PetscInt    *perm  = perms ? perms[p] : NULL;
7144           const PetscScalar *flip  = flips ? flips[p] : NULL;
7145           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array));
7146         }
7147         break;
7148       case ADD_ALL_VALUES:
7149         for (p = 0; p < numPoints; p++) {
7150           const PetscInt     point = points[2 * p];
7151           const PetscInt    *perm  = perms ? perms[p] : NULL;
7152           const PetscScalar *flip  = flips ? flips[p] : NULL;
7153           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array));
7154         }
7155         break;
7156       case ADD_BC_VALUES:
7157         for (p = 0; p < numPoints; p++) {
7158           const PetscInt     point = points[2 * p];
7159           const PetscInt    *perm  = perms ? perms[p] : NULL;
7160           const PetscScalar *flip  = flips ? flips[p] : NULL;
7161           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array));
7162         }
7163         break;
7164       default:
7165         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7166       }
7167       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7168     }
7169   } else {
7170     PetscInt            dof, off;
7171     const PetscInt    **perms = NULL;
7172     const PetscScalar **flips = NULL;
7173 
7174     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
7175     switch (mode) {
7176     case INSERT_VALUES:
7177       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7178         const PetscInt     point = points[2 * p];
7179         const PetscInt    *perm  = perms ? perms[p] : NULL;
7180         const PetscScalar *flip  = flips ? flips[p] : NULL;
7181         PetscCall(PetscSectionGetDof(section, point, &dof));
7182         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array));
7183       }
7184       break;
7185     case INSERT_ALL_VALUES:
7186       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7187         const PetscInt     point = points[2 * p];
7188         const PetscInt    *perm  = perms ? perms[p] : NULL;
7189         const PetscScalar *flip  = flips ? flips[p] : NULL;
7190         PetscCall(PetscSectionGetDof(section, point, &dof));
7191         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array));
7192       }
7193       break;
7194     case INSERT_BC_VALUES:
7195       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7196         const PetscInt     point = points[2 * p];
7197         const PetscInt    *perm  = perms ? perms[p] : NULL;
7198         const PetscScalar *flip  = flips ? flips[p] : NULL;
7199         PetscCall(PetscSectionGetDof(section, point, &dof));
7200         PetscCall(updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array));
7201       }
7202       break;
7203     case ADD_VALUES:
7204       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7205         const PetscInt     point = points[2 * p];
7206         const PetscInt    *perm  = perms ? perms[p] : NULL;
7207         const PetscScalar *flip  = flips ? flips[p] : NULL;
7208         PetscCall(PetscSectionGetDof(section, point, &dof));
7209         PetscCall(updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array));
7210       }
7211       break;
7212     case ADD_ALL_VALUES:
7213       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7214         const PetscInt     point = points[2 * p];
7215         const PetscInt    *perm  = perms ? perms[p] : NULL;
7216         const PetscScalar *flip  = flips ? flips[p] : NULL;
7217         PetscCall(PetscSectionGetDof(section, point, &dof));
7218         PetscCall(updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array));
7219       }
7220       break;
7221     case ADD_BC_VALUES:
7222       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7223         const PetscInt     point = points[2 * p];
7224         const PetscInt    *perm  = perms ? perms[p] : NULL;
7225         const PetscScalar *flip  = flips ? flips[p] : NULL;
7226         PetscCall(PetscSectionGetDof(section, point, &dof));
7227         PetscCall(updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array));
7228       }
7229       break;
7230     default:
7231       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7232     }
7233     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
7234   }
7235   /* Cleanup points */
7236   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7237   /* Cleanup array */
7238   PetscCall(VecRestoreArray(v, &array));
7239   PetscFunctionReturn(PETSC_SUCCESS);
7240 }
7241 
7242 /* Check whether the given point is in the label. If not, update the offset to skip this point */
7243 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
7244 {
7245   PetscFunctionBegin;
7246   *contains = PETSC_TRUE;
7247   if (label) {
7248     PetscInt fdof;
7249 
7250     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
7251     if (!*contains) {
7252       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7253       *offset += fdof;
7254       PetscFunctionReturn(PETSC_SUCCESS);
7255     }
7256   }
7257   PetscFunctionReturn(PETSC_SUCCESS);
7258 }
7259 
7260 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
7261 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)
7262 {
7263   PetscSection    clSection;
7264   IS              clPoints;
7265   PetscScalar    *array;
7266   PetscInt       *points = NULL;
7267   const PetscInt *clp;
7268   PetscInt        numFields, numPoints, p;
7269   PetscInt        offset = 0, f;
7270 
7271   PetscFunctionBeginHot;
7272   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7273   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7274   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7275   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7276   PetscCall(PetscSectionGetNumFields(section, &numFields));
7277   /* Get points */
7278   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7279   /* Get array */
7280   PetscCall(VecGetArray(v, &array));
7281   /* Get values */
7282   for (f = 0; f < numFields; ++f) {
7283     const PetscInt    **perms = NULL;
7284     const PetscScalar **flips = NULL;
7285     PetscBool           contains;
7286 
7287     if (!fieldActive[f]) {
7288       for (p = 0; p < numPoints * 2; p += 2) {
7289         PetscInt fdof;
7290         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7291         offset += fdof;
7292       }
7293       continue;
7294     }
7295     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7296     switch (mode) {
7297     case INSERT_VALUES:
7298       for (p = 0; p < numPoints; p++) {
7299         const PetscInt     point = points[2 * p];
7300         const PetscInt    *perm  = perms ? perms[p] : NULL;
7301         const PetscScalar *flip  = flips ? flips[p] : NULL;
7302         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7303         if (!contains) continue;
7304         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
7305       }
7306       break;
7307     case INSERT_ALL_VALUES:
7308       for (p = 0; p < numPoints; p++) {
7309         const PetscInt     point = points[2 * p];
7310         const PetscInt    *perm  = perms ? perms[p] : NULL;
7311         const PetscScalar *flip  = flips ? flips[p] : NULL;
7312         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7313         if (!contains) continue;
7314         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
7315       }
7316       break;
7317     case INSERT_BC_VALUES:
7318       for (p = 0; p < numPoints; p++) {
7319         const PetscInt     point = points[2 * p];
7320         const PetscInt    *perm  = perms ? perms[p] : NULL;
7321         const PetscScalar *flip  = flips ? flips[p] : NULL;
7322         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7323         if (!contains) continue;
7324         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
7325       }
7326       break;
7327     case ADD_VALUES:
7328       for (p = 0; p < numPoints; p++) {
7329         const PetscInt     point = points[2 * p];
7330         const PetscInt    *perm  = perms ? perms[p] : NULL;
7331         const PetscScalar *flip  = flips ? flips[p] : NULL;
7332         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7333         if (!contains) continue;
7334         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
7335       }
7336       break;
7337     case ADD_ALL_VALUES:
7338       for (p = 0; p < numPoints; p++) {
7339         const PetscInt     point = points[2 * p];
7340         const PetscInt    *perm  = perms ? perms[p] : NULL;
7341         const PetscScalar *flip  = flips ? flips[p] : NULL;
7342         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7343         if (!contains) continue;
7344         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
7345       }
7346       break;
7347     default:
7348       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7349     }
7350     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7351   }
7352   /* Cleanup points */
7353   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7354   /* Cleanup array */
7355   PetscCall(VecRestoreArray(v, &array));
7356   PetscFunctionReturn(PETSC_SUCCESS);
7357 }
7358 
7359 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
7360 {
7361   PetscMPIInt rank;
7362   PetscInt    i, j;
7363 
7364   PetscFunctionBegin;
7365   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7366   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
7367   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
7368   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
7369   numCIndices = numCIndices ? numCIndices : numRIndices;
7370   if (!values) PetscFunctionReturn(PETSC_SUCCESS);
7371   for (i = 0; i < numRIndices; i++) {
7372     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
7373     for (j = 0; j < numCIndices; j++) {
7374 #if defined(PETSC_USE_COMPLEX)
7375       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
7376 #else
7377       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
7378 #endif
7379     }
7380     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
7381   }
7382   PetscFunctionReturn(PETSC_SUCCESS);
7383 }
7384 
7385 /*
7386   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
7387 
7388   Input Parameters:
7389 + section - The section for this data layout
7390 . islocal - Is the section (and thus indices being requested) local or global?
7391 . point   - The point contributing dofs with these indices
7392 . off     - The global offset of this point
7393 . loff    - The local offset of each field
7394 . setBC   - The flag determining whether to include indices of boundary values
7395 . perm    - A permutation of the dofs on this point, or NULL
7396 - indperm - A permutation of the entire indices array, or NULL
7397 
7398   Output Parameter:
7399 . indices - Indices for dofs on this point
7400 
7401   Level: developer
7402 
7403   Note: The indices could be local or global, depending on the value of 'off'.
7404 */
7405 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
7406 {
7407   PetscInt        dof;   /* The number of unknowns on this point */
7408   PetscInt        cdof;  /* The number of constraints on this point */
7409   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7410   PetscInt        cind = 0, k;
7411 
7412   PetscFunctionBegin;
7413   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7414   PetscCall(PetscSectionGetDof(section, point, &dof));
7415   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
7416   if (!cdof || setBC) {
7417     for (k = 0; k < dof; ++k) {
7418       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7419       const PetscInt ind    = indperm ? indperm[preind] : preind;
7420 
7421       indices[ind] = off + k;
7422     }
7423   } else {
7424     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
7425     for (k = 0; k < dof; ++k) {
7426       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7427       const PetscInt ind    = indperm ? indperm[preind] : preind;
7428 
7429       if ((cind < cdof) && (k == cdofs[cind])) {
7430         /* Insert check for returning constrained indices */
7431         indices[ind] = -(off + k + 1);
7432         ++cind;
7433       } else {
7434         indices[ind] = off + k - (islocal ? 0 : cind);
7435       }
7436     }
7437   }
7438   *loff += dof;
7439   PetscFunctionReturn(PETSC_SUCCESS);
7440 }
7441 
7442 /*
7443  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
7444 
7445  Input Parameters:
7446 + section - a section (global or local)
7447 - islocal - `PETSC_TRUE` if requesting local indices (i.e., section is local); `PETSC_FALSE` for global
7448 . point - point within section
7449 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
7450 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
7451 . setBC - identify constrained (boundary condition) points via involution.
7452 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
7453 . permsoff - offset
7454 - indperm - index permutation
7455 
7456  Output Parameter:
7457 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
7458 . indices - array to hold indices (as defined by section) of each dof associated with point
7459 
7460  Notes:
7461  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
7462  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
7463  in the local vector.
7464 
7465  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
7466  significant).  It is invalid to call with a global section and setBC=true.
7467 
7468  Developer Note:
7469  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
7470  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
7471  offset could be obtained from the section instead of passing it explicitly as we do now.
7472 
7473  Example:
7474  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
7475  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
7476  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
7477  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.
7478 
7479  Level: developer
7480 */
7481 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[])
7482 {
7483   PetscInt numFields, foff, f;
7484 
7485   PetscFunctionBegin;
7486   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7487   PetscCall(PetscSectionGetNumFields(section, &numFields));
7488   for (f = 0, foff = 0; f < numFields; ++f) {
7489     PetscInt        fdof, cfdof;
7490     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7491     PetscInt        cind = 0, b;
7492     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7493 
7494     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7495     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7496     if (!cfdof || setBC) {
7497       for (b = 0; b < fdof; ++b) {
7498         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7499         const PetscInt ind    = indperm ? indperm[preind] : preind;
7500 
7501         indices[ind] = off + foff + b;
7502       }
7503     } else {
7504       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7505       for (b = 0; b < fdof; ++b) {
7506         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7507         const PetscInt ind    = indperm ? indperm[preind] : preind;
7508 
7509         if ((cind < cfdof) && (b == fcdofs[cind])) {
7510           indices[ind] = -(off + foff + b + 1);
7511           ++cind;
7512         } else {
7513           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7514         }
7515       }
7516     }
7517     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7518     foffs[f] += fdof;
7519   }
7520   PetscFunctionReturn(PETSC_SUCCESS);
7521 }
7522 
7523 /*
7524   This version believes the globalSection offsets for each field, rather than just the point offset
7525 
7526  . foffs - The offset into 'indices' for each field, since it is segregated by field
7527 
7528  Notes:
7529  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7530  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7531 */
7532 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7533 {
7534   PetscInt numFields, foff, f;
7535 
7536   PetscFunctionBegin;
7537   PetscCall(PetscSectionGetNumFields(section, &numFields));
7538   for (f = 0; f < numFields; ++f) {
7539     PetscInt        fdof, cfdof;
7540     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7541     PetscInt        cind = 0, b;
7542     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7543 
7544     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7545     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7546     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7547     if (!cfdof) {
7548       for (b = 0; b < fdof; ++b) {
7549         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7550         const PetscInt ind    = indperm ? indperm[preind] : preind;
7551 
7552         indices[ind] = foff + b;
7553       }
7554     } else {
7555       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7556       for (b = 0; b < fdof; ++b) {
7557         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7558         const PetscInt ind    = indperm ? indperm[preind] : preind;
7559 
7560         if ((cind < cfdof) && (b == fcdofs[cind])) {
7561           indices[ind] = -(foff + b + 1);
7562           ++cind;
7563         } else {
7564           indices[ind] = foff + b - cind;
7565         }
7566       }
7567     }
7568     foffs[f] += fdof;
7569   }
7570   PetscFunctionReturn(PETSC_SUCCESS);
7571 }
7572 
7573 static PetscErrorCode DMPlexAnchorsGetSubMatIndices(PetscInt nPoints, const PetscInt pnts[], PetscSection section, PetscSection cSec, PetscInt tmpIndices[], PetscInt fieldOffsets[], PetscInt indices[], const PetscInt ***perms)
7574 {
7575   PetscInt numFields, sStart, sEnd, cStart, cEnd;
7576 
7577   PetscFunctionBegin;
7578   PetscCall(PetscSectionGetNumFields(section, &numFields));
7579   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7580   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7581   for (PetscInt p = 0; p < nPoints; p++) {
7582     PetscInt     b       = pnts[2 * p];
7583     PetscInt     bSecDof = 0, bOff;
7584     PetscInt     cSecDof = 0;
7585     PetscSection indices_section;
7586 
7587     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7588     if (!bSecDof) continue;
7589     if (b >= cStart && b < cEnd) PetscCall(PetscSectionGetDof(cSec, b, &cSecDof));
7590     indices_section = cSecDof > 0 ? cSec : section;
7591     if (numFields) {
7592       PetscInt fStart[32], fEnd[32];
7593 
7594       fStart[0] = 0;
7595       fEnd[0]   = 0;
7596       for (PetscInt f = 0; f < numFields; f++) {
7597         PetscInt fDof = 0;
7598 
7599         PetscCall(PetscSectionGetFieldDof(indices_section, b, f, &fDof));
7600         fStart[f + 1] = fStart[f] + fDof;
7601         fEnd[f + 1]   = fStart[f + 1];
7602       }
7603       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7604       // only apply permutations on one side
7605       PetscCall(DMPlexGetIndicesPointFields_Internal(indices_section, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, perms ? p : -1, NULL, tmpIndices));
7606       for (PetscInt f = 0; f < numFields; f++) {
7607         for (PetscInt i = fStart[f]; i < fEnd[f]; i++) { indices[fieldOffsets[f]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1); }
7608       }
7609     } else {
7610       PetscInt bEnd = 0;
7611 
7612       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7613       PetscCall(DMPlexGetIndicesPoint_Internal(indices_section, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, tmpIndices));
7614 
7615       for (PetscInt i = 0; i < bEnd; i++) indices[fieldOffsets[0]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1);
7616     }
7617   }
7618   PetscFunctionReturn(PETSC_SUCCESS);
7619 }
7620 
7621 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[])
7622 {
7623   Mat             cMat;
7624   PetscSection    aSec, cSec;
7625   IS              aIS;
7626   PetscInt        aStart = -1, aEnd = -1;
7627   PetscInt        sStart = -1, sEnd = -1;
7628   PetscInt        cStart = -1, cEnd = -1;
7629   const PetscInt *anchors;
7630   PetscInt        numFields, p;
7631   PetscInt        newNumPoints = 0, newNumIndices = 0;
7632   PetscInt       *newPoints, *indices, *newIndices, *tmpIndices, *tmpNewIndices;
7633   PetscInt        oldOffsets[32];
7634   PetscInt        newOffsets[32];
7635   PetscInt        oldOffsetsCopy[32];
7636   PetscInt        newOffsetsCopy[32];
7637   PetscScalar    *modMat         = NULL;
7638   PetscBool       anyConstrained = PETSC_FALSE;
7639 
7640   PetscFunctionBegin;
7641   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7642   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7643   PetscCall(PetscSectionGetNumFields(section, &numFields));
7644 
7645   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7646   /* if there are point-to-point constraints */
7647   if (aSec) {
7648     PetscCall(PetscArrayzero(newOffsets, 32));
7649     PetscCall(PetscArrayzero(oldOffsets, 32));
7650     PetscCall(ISGetIndices(aIS, &anchors));
7651     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7652     PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7653     /* figure out how many points are going to be in the new element matrix
7654      * (we allow double counting, because it's all just going to be summed
7655      * into the global matrix anyway) */
7656     for (p = 0; p < 2 * numPoints; p += 2) {
7657       PetscInt b    = points[p];
7658       PetscInt bDof = 0, bSecDof = 0;
7659 
7660       if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7661       if (!bSecDof) continue;
7662 
7663       for (PetscInt f = 0; f < numFields; f++) {
7664         PetscInt fDof = 0;
7665 
7666         PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7667         oldOffsets[f + 1] += fDof;
7668       }
7669       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7670       if (bDof) {
7671         /* this point is constrained */
7672         /* it is going to be replaced by its anchors */
7673         PetscInt bOff, q;
7674 
7675         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7676         for (q = 0; q < bDof; q++) {
7677           PetscInt a    = anchors[bOff + q];
7678           PetscInt aDof = 0;
7679 
7680           if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7681           if (aDof) {
7682             anyConstrained = PETSC_TRUE;
7683             newNumPoints += 1;
7684           }
7685           newNumIndices += aDof;
7686           for (PetscInt f = 0; f < numFields; ++f) {
7687             PetscInt fDof = 0;
7688 
7689             if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7690             newOffsets[f + 1] += fDof;
7691           }
7692         }
7693       } else {
7694         /* this point is not constrained */
7695         newNumPoints++;
7696         newNumIndices += bSecDof;
7697         for (PetscInt f = 0; f < numFields; ++f) {
7698           PetscInt fDof;
7699 
7700           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7701           newOffsets[f + 1] += fDof;
7702         }
7703       }
7704     }
7705   }
7706   if (!anyConstrained) {
7707     if (outNumPoints) *outNumPoints = 0;
7708     if (outNumIndices) *outNumIndices = 0;
7709     if (outPoints) *outPoints = NULL;
7710     if (outMat) *outMat = NULL;
7711     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7712     PetscFunctionReturn(PETSC_SUCCESS);
7713   }
7714 
7715   if (outNumPoints) *outNumPoints = newNumPoints;
7716   if (outNumIndices) *outNumIndices = newNumIndices;
7717 
7718   for (PetscInt f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7719   for (PetscInt f = 0; f < numFields; ++f) oldOffsets[f + 1] += oldOffsets[f];
7720 
7721   if (!outPoints && !outMat) {
7722     if (offsets) {
7723       for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7724     }
7725     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7726     PetscFunctionReturn(PETSC_SUCCESS);
7727   }
7728 
7729   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7730   PetscCheck(!numFields || oldOffsets[numFields] == numIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, oldOffsets[numFields], numIndices);
7731 
7732   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7733   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7734 
7735   /* output arrays */
7736   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7737   PetscCall(PetscArrayzero(newPoints, 2 * newNumPoints));
7738 
7739   // get the new Points
7740   for (PetscInt p = 0, newP = 0; p < numPoints; p++) {
7741     PetscInt b    = points[2 * p];
7742     PetscInt bDof = 0, bSecDof = 0, bOff;
7743 
7744     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7745     if (!bSecDof) continue;
7746     if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7747     if (bDof) {
7748       PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7749       for (PetscInt q = 0; q < bDof; q++) {
7750         PetscInt a = anchors[bOff + q], aDof = 0;
7751 
7752         if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7753         if (aDof) {
7754           newPoints[2 * newP]     = a;
7755           newPoints[2 * newP + 1] = 0; // orientations are accounted for in constructing the matrix, newly added points are in default orientation
7756           newP++;
7757         }
7758       }
7759     } else {
7760       newPoints[2 * newP]     = b;
7761       newPoints[2 * newP + 1] = points[2 * p + 1];
7762       newP++;
7763     }
7764   }
7765 
7766   if (outMat) {
7767     PetscScalar *tmpMat;
7768     PetscCall(PetscArraycpy(oldOffsetsCopy, oldOffsets, 32));
7769     PetscCall(PetscArraycpy(newOffsetsCopy, newOffsets, 32));
7770 
7771     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &indices));
7772     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7773     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7774     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7775 
7776     for (PetscInt i = 0; i < numIndices; i++) indices[i] = -1;
7777     for (PetscInt i = 0; i < newNumIndices; i++) newIndices[i] = -1;
7778 
7779     PetscCall(DMPlexAnchorsGetSubMatIndices(numPoints, points, section, cSec, tmpIndices, oldOffsetsCopy, indices, perms));
7780     PetscCall(DMPlexAnchorsGetSubMatIndices(newNumPoints, newPoints, section, section, tmpNewIndices, newOffsetsCopy, newIndices, NULL));
7781 
7782     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
7783     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7784     PetscCall(PetscArrayzero(modMat, newNumIndices * numIndices));
7785     // for each field, insert the anchor modification into modMat
7786     for (PetscInt f = 0; f < PetscMax(1, numFields); f++) {
7787       PetscInt fStart    = oldOffsets[f];
7788       PetscInt fNewStart = newOffsets[f];
7789       for (PetscInt p = 0, newP = 0, o = fStart, oNew = fNewStart; p < numPoints; p++) {
7790         PetscInt b    = points[2 * p];
7791         PetscInt bDof = 0, bSecDof = 0, bOff;
7792 
7793         if (b >= sStart && b < sEnd) {
7794           if (numFields) {
7795             PetscCall(PetscSectionGetFieldDof(section, b, f, &bSecDof));
7796           } else {
7797             PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7798           }
7799         }
7800         if (!bSecDof) continue;
7801         if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7802         if (bDof) {
7803           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7804           for (PetscInt q = 0; q < bDof; q++, newP++) {
7805             PetscInt a = anchors[bOff + q], aDof = 0;
7806 
7807             if (a >= sStart && a < sEnd) {
7808               if (numFields) {
7809                 PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
7810               } else {
7811                 PetscCall(PetscSectionGetDof(section, a, &aDof));
7812               }
7813             }
7814             if (aDof) {
7815               PetscCall(MatGetValues(cMat, bSecDof, &indices[o], aDof, &newIndices[oNew], tmpMat));
7816               for (PetscInt d = 0; d < bSecDof; d++) {
7817                 for (PetscInt e = 0; e < aDof; e++) modMat[(o + d) * newNumIndices + oNew + e] = tmpMat[d * aDof + e];
7818               }
7819             }
7820             oNew += aDof;
7821           }
7822         } else {
7823           // Insert the identity matrix in this block
7824           for (PetscInt d = 0; d < bSecDof; d++) modMat[(o + d) * newNumIndices + oNew + d] = 1;
7825           oNew += bSecDof;
7826           newP++;
7827         }
7828         o += bSecDof;
7829       }
7830     }
7831 
7832     *outMat = modMat;
7833 
7834     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7835     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7836     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7837     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7838     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &indices));
7839   }
7840   PetscCall(ISRestoreIndices(aIS, &anchors));
7841 
7842   /* output */
7843   if (outPoints) {
7844     *outPoints = newPoints;
7845   } else {
7846     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7847   }
7848   for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7849   PetscFunctionReturn(PETSC_SUCCESS);
7850 }
7851 
7852 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)
7853 {
7854   PetscScalar *modMat        = NULL;
7855   PetscInt     newNumIndices = -1;
7856 
7857   PetscFunctionBegin;
7858   /* 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.
7859      modMat is that matrix C */
7860   PetscCall(DMPlexAnchorsGetSubMatModification(dm, section, numPoints, numIndices, points, perms, outNumPoints, &newNumIndices, outPoints, offsets, outValues ? &modMat : NULL));
7861   if (outNumIndices) *outNumIndices = newNumIndices;
7862   if (modMat) {
7863     const PetscScalar *newValues = values;
7864 
7865     if (multiplyRight) {
7866       PetscScalar *newNewValues = NULL;
7867       PetscBLASInt M            = newNumIndices;
7868       PetscBLASInt N            = numRows;
7869       PetscBLASInt K            = numIndices;
7870       PetscScalar  a = 1.0, b = 0.0;
7871 
7872       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);
7873 
7874       PetscCall(DMGetWorkArray(dm, numRows * newNumIndices, MPIU_SCALAR, &newNewValues));
7875       // row-major to column-major conversion, right multiplication becomes left multiplication
7876       PetscCallBLAS("BLASgemm", BLASgemm_("N", "N", &M, &N, &K, &a, modMat, &M, newValues, &K, &b, newNewValues, &M));
7877 
7878       numCols   = newNumIndices;
7879       newValues = newNewValues;
7880     }
7881 
7882     if (multiplyLeft) {
7883       PetscScalar *newNewValues = NULL;
7884       PetscBLASInt M            = numCols;
7885       PetscBLASInt N            = newNumIndices;
7886       PetscBLASInt K            = numIndices;
7887       PetscScalar  a = 1.0, b = 0.0;
7888 
7889       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);
7890 
7891       PetscCall(DMGetWorkArray(dm, newNumIndices * numCols, MPIU_SCALAR, &newNewValues));
7892       // row-major to column-major conversion, left multiplication becomes right multiplication
7893       PetscCallBLAS("BLASgemm", BLASgemm_("N", "T", &M, &N, &K, &a, newValues, &M, modMat, &N, &b, newNewValues, &M));
7894       if (newValues != values) PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &newValues));
7895       newValues = newNewValues;
7896     }
7897     *outValues = (PetscScalar *)newValues;
7898     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
7899   }
7900   PetscFunctionReturn(PETSC_SUCCESS);
7901 }
7902 
7903 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)
7904 {
7905   PetscFunctionBegin;
7906   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, numPoints, numIndices, points, perms, numIndices, numIndices, values, outNumPoints, outNumIndices, outPoints, outValues, offsets, PETSC_TRUE, multiplyLeft));
7907   PetscFunctionReturn(PETSC_SUCCESS);
7908 }
7909 
7910 static PetscErrorCode DMPlexGetClosureIndicesSize_Internal(DM dm, PetscSection section, PetscInt point, PetscInt *closureSize)
7911 {
7912   /* Closure ordering */
7913   PetscSection    clSection;
7914   IS              clPoints;
7915   const PetscInt *clp;
7916   PetscInt       *points;
7917   PetscInt        Ncl, Ni = 0;
7918 
7919   PetscFunctionBeginHot;
7920   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7921   for (PetscInt p = 0; p < Ncl * 2; p += 2) {
7922     PetscInt dof;
7923 
7924     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7925     Ni += dof;
7926   }
7927   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7928   *closureSize = Ni;
7929   PetscFunctionReturn(PETSC_SUCCESS);
7930 }
7931 
7932 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)
7933 {
7934   /* Closure ordering */
7935   PetscSection    clSection;
7936   IS              clPoints;
7937   const PetscInt *clp;
7938   PetscInt       *points;
7939   const PetscInt *clperm = NULL;
7940   /* Dof permutation and sign flips */
7941   const PetscInt    **perms[32] = {NULL};
7942   const PetscScalar **flips[32] = {NULL};
7943   PetscScalar        *valCopy   = NULL;
7944   /* Hanging node constraints */
7945   PetscInt    *pointsC = NULL;
7946   PetscScalar *valuesC = NULL;
7947   PetscInt     NclC, NiC;
7948 
7949   PetscInt *idx;
7950   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7951   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7952   PetscInt  idxStart, idxEnd;
7953   PetscInt  nRows, nCols;
7954 
7955   PetscFunctionBeginHot;
7956   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7957   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7958   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7959   PetscAssertPointer(numRows, 6);
7960   PetscAssertPointer(numCols, 7);
7961   if (indices) PetscAssertPointer(indices, 8);
7962   if (outOffsets) PetscAssertPointer(outOffsets, 9);
7963   if (values) PetscAssertPointer(values, 10);
7964   PetscCall(PetscSectionGetNumFields(section, &Nf));
7965   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7966   PetscCall(PetscArrayzero(offsets, 32));
7967   /* 1) Get points in closure */
7968   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7969   if (useClPerm) {
7970     PetscInt depth, clsize;
7971     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7972     for (clsize = 0, p = 0; p < Ncl; p++) {
7973       PetscInt dof;
7974       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7975       clsize += dof;
7976     }
7977     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7978   }
7979   /* 2) Get number of indices on these points and field offsets from section */
7980   for (p = 0; p < Ncl * 2; p += 2) {
7981     PetscInt dof, fdof;
7982 
7983     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7984     for (f = 0; f < Nf; ++f) {
7985       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7986       offsets[f + 1] += fdof;
7987     }
7988     Ni += dof;
7989   }
7990   if (*numRows == -1) *numRows = Ni;
7991   if (*numCols == -1) *numCols = Ni;
7992   nRows = *numRows;
7993   nCols = *numCols;
7994   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
7995   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7996   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7997   if (multiplyRight) PetscCheck(nCols == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " columns, got %" PetscInt_FMT, Ni, nCols);
7998   if (multiplyLeft) PetscCheck(nRows == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " rows, got %" PetscInt_FMT, Ni, nRows);
7999   for (f = 0; f < PetscMax(1, Nf); ++f) {
8000     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8001     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
8002     /* may need to apply sign changes to the element matrix */
8003     if (values && flips[f]) {
8004       PetscInt foffset = offsets[f];
8005 
8006       for (p = 0; p < Ncl; ++p) {
8007         PetscInt           pnt  = points[2 * p], fdof;
8008         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
8009 
8010         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
8011         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
8012         if (flip) {
8013           PetscInt i, j, k;
8014 
8015           if (!valCopy) {
8016             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8017             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
8018             *values = valCopy;
8019           }
8020           for (i = 0; i < fdof; ++i) {
8021             PetscScalar fval = flip[i];
8022 
8023             if (multiplyRight) {
8024               for (k = 0; k < nRows; ++k) { valCopy[Ni * k + (foffset + i)] *= fval; }
8025             }
8026             if (multiplyLeft) {
8027               for (k = 0; k < nCols; ++k) { valCopy[nCols * (foffset + i) + k] *= fval; }
8028             }
8029           }
8030         }
8031         foffset += fdof;
8032       }
8033     }
8034   }
8035   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
8036   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, Ncl, Ni, points, perms, nRows, nCols, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, multiplyRight, multiplyLeft));
8037   if (NclC) {
8038     if (multiplyRight) { *numCols = nCols = NiC; }
8039     if (multiplyLeft) { *numRows = nRows = NiC; }
8040     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8041     for (f = 0; f < PetscMax(1, Nf); ++f) {
8042       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8043       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8044     }
8045     for (f = 0; f < PetscMax(1, Nf); ++f) {
8046       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
8047       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
8048     }
8049     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8050     Ncl    = NclC;
8051     Ni     = NiC;
8052     points = pointsC;
8053     if (values) *values = valuesC;
8054   }
8055   /* 5) Calculate indices */
8056   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
8057   PetscCall(PetscSectionGetChart(idxSection, &idxStart, &idxEnd));
8058   if (Nf) {
8059     PetscInt  idxOff;
8060     PetscBool useFieldOffsets;
8061 
8062     if (outOffsets) {
8063       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
8064     }
8065     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
8066     if (useFieldOffsets) {
8067       for (p = 0; p < Ncl; ++p) {
8068         const PetscInt pnt = points[p * 2];
8069 
8070         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
8071       }
8072     } else {
8073       for (p = 0; p < Ncl; ++p) {
8074         const PetscInt pnt = points[p * 2];
8075 
8076         if (pnt < idxStart || pnt >= idxEnd) continue;
8077         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8078         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8079          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
8080          * global section. */
8081         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
8082       }
8083     }
8084   } else {
8085     PetscInt off = 0, idxOff;
8086 
8087     for (p = 0; p < Ncl; ++p) {
8088       const PetscInt  pnt  = points[p * 2];
8089       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
8090 
8091       if (pnt < idxStart || pnt >= idxEnd) continue;
8092       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8093       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8094        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
8095       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
8096     }
8097   }
8098   /* 6) Cleanup */
8099   for (f = 0; f < PetscMax(1, Nf); ++f) {
8100     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8101     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8102   }
8103   if (NclC) {
8104     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
8105   } else {
8106     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8107   }
8108 
8109   if (indices) *indices = idx;
8110   PetscFunctionReturn(PETSC_SUCCESS);
8111 }
8112 
8113 /*@C
8114   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
8115 
8116   Not collective
8117 
8118   Input Parameters:
8119 + dm         - The `DM`
8120 . section    - The `PetscSection` describing the points (a local section)
8121 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8122 . point      - The point defining the closure
8123 - useClPerm  - Use the closure point permutation if available
8124 
8125   Output Parameters:
8126 + numIndices - The number of dof indices in the closure of point with the input sections
8127 . indices    - The dof indices
8128 . outOffsets - Array to write the field offsets into, or `NULL`
8129 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8130 
8131   Level: advanced
8132 
8133   Notes:
8134   Call `DMPlexRestoreClosureIndices()` to free allocated memory
8135 
8136   If `idxSection` is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8137   of those indices is not significant.  If `idxSection` is local, the constrained dofs will yield the involution -(idx+1)
8138   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8139   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when `idxSection` == section, otherwise global
8140   indices (with the above semantics) are implied.
8141 
8142 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
8143           `PetscSection`, `DMGetGlobalSection()`
8144 @*/
8145 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
8146 {
8147   PetscInt numRows = -1, numCols = -1;
8148 
8149   PetscFunctionBeginHot;
8150   PetscCall(DMPlexGetClosureIndices_Internal(dm, section, idxSection, point, useClPerm, &numRows, &numCols, indices, outOffsets, values, PETSC_TRUE, PETSC_TRUE));
8151   PetscCheck(numRows == numCols, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Symmetric matrix transformation produces rectangular dimensions (%" PetscInt_FMT ", %" PetscInt_FMT ")", numRows, numCols);
8152   *numIndices = numRows;
8153   PetscFunctionReturn(PETSC_SUCCESS);
8154 }
8155 
8156 /*@C
8157   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
8158 
8159   Not collective
8160 
8161   Input Parameters:
8162 + dm         - The `DM`
8163 . section    - The `PetscSection` describing the points (a local section)
8164 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8165 . point      - The point defining the closure
8166 - useClPerm  - Use the closure point permutation if available
8167 
8168   Output Parameters:
8169 + numIndices - The number of dof indices in the closure of point with the input sections
8170 . indices    - The dof indices
8171 . outOffsets - Array to write the field offsets into, or `NULL`
8172 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8173 
8174   Level: advanced
8175 
8176   Notes:
8177   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
8178 
8179   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8180   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
8181   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8182   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
8183   indices (with the above semantics) are implied.
8184 
8185 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
8186 @*/
8187 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
8188 {
8189   PetscFunctionBegin;
8190   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8191   PetscAssertPointer(indices, 7);
8192   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
8193   PetscFunctionReturn(PETSC_SUCCESS);
8194 }
8195 
8196 PetscErrorCode DMPlexMatSetClosure_Internal(DM dm, PetscSection section, PetscSection globalSection, PetscBool useClPerm, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8197 {
8198   DM_Plex           *mesh = (DM_Plex *)dm->data;
8199   PetscInt          *indices;
8200   PetscInt           numIndices;
8201   const PetscScalar *valuesOrig = values;
8202   PetscErrorCode     ierr;
8203 
8204   PetscFunctionBegin;
8205   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8206   if (!section) PetscCall(DMGetLocalSection(dm, &section));
8207   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
8208   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
8209   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
8210   PetscValidHeaderSpecific(A, MAT_CLASSID, 5);
8211 
8212   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, useClPerm, &numIndices, &indices, NULL, (PetscScalar **)&values));
8213 
8214   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
8215   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8216   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
8217   if (ierr) {
8218     PetscMPIInt rank;
8219 
8220     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8221     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8222     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
8223     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8224     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8225     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
8226   }
8227   if (mesh->printFEM > 1) {
8228     PetscInt i;
8229     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
8230     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
8231     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8232   }
8233 
8234   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8235   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8236   PetscFunctionReturn(PETSC_SUCCESS);
8237 }
8238 
8239 /*@C
8240   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
8241 
8242   Not collective
8243 
8244   Input Parameters:
8245 + dm            - The `DM`
8246 . section       - The section describing the layout in `v`, or `NULL` to use the default section
8247 . globalSection - The section describing the layout in `v`, or `NULL` to use the default global section
8248 . A             - The matrix
8249 . point         - The point in the `DM`
8250 . values        - The array of values
8251 - mode          - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8252 
8253   Level: intermediate
8254 
8255 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8256 @*/
8257 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8258 {
8259   PetscFunctionBegin;
8260   PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, PETSC_TRUE, A, point, values, mode));
8261   PetscFunctionReturn(PETSC_SUCCESS);
8262 }
8263 
8264 /*@C
8265   DMPlexMatSetClosureGeneral - Set an array of the values on the closure of 'point' using a different row and column section
8266 
8267   Not collective
8268 
8269   Input Parameters:
8270 + dmRow            - The `DM` for the row fields
8271 . sectionRow       - The section describing the layout, or `NULL` to use the default section in `dmRow`
8272 . useRowPerm       - The flag to use the closure permutation of the `dmRow` if available
8273 . globalSectionRow - The section describing the layout, or `NULL` to use the default global section in `dmRow`
8274 . dmCol            - The `DM` for the column fields
8275 . sectionCol       - The section describing the layout, or `NULL` to use the default section in `dmCol`
8276 . useColPerm       - The flag to use the closure permutation of the `dmCol` if available
8277 . globalSectionCol - The section describing the layout, or `NULL` to use the default global section in `dmCol`
8278 . A                - The matrix
8279 . point            - The point in the `DM`
8280 . values           - The array of values
8281 - mode             - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8282 
8283   Level: intermediate
8284 
8285 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8286 @*/
8287 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)
8288 {
8289   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
8290   PetscInt          *indicesRow, *indicesCol;
8291   PetscInt           numIndicesRow = -1, numIndicesCol = -1;
8292   const PetscScalar *valuesV0 = values, *valuesV1, *valuesV2;
8293 
8294   PetscErrorCode ierr;
8295 
8296   PetscFunctionBegin;
8297   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
8298   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
8299   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
8300   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
8301   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
8302   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 5);
8303   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
8304   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 6);
8305   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
8306   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 7);
8307   PetscValidHeaderSpecific(A, MAT_CLASSID, 9);
8308 
8309   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmRow, sectionRow, point, &numIndicesRow));
8310   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmCol, sectionCol, point, &numIndicesCol));
8311   valuesV1 = valuesV0;
8312   PetscCall(DMPlexGetClosureIndices_Internal(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV1, PETSC_FALSE, PETSC_TRUE));
8313   valuesV2 = valuesV1;
8314   PetscCall(DMPlexGetClosureIndices_Internal(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesRow, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2, PETSC_TRUE, PETSC_FALSE));
8315 
8316   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2));
8317   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8318   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2, mode);
8319   if (ierr) {
8320     PetscMPIInt rank;
8321 
8322     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8323     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8324     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
8325     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV2));
8326     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8327     if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8328     if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8329   }
8330 
8331   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2));
8332   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8333   if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8334   if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8335   PetscFunctionReturn(PETSC_SUCCESS);
8336 }
8337 
8338 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8339 {
8340   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
8341   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8342   PetscInt       *cpoints = NULL;
8343   PetscInt       *findices, *cindices;
8344   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8345   PetscInt        foffsets[32], coffsets[32];
8346   DMPolytopeType  ct;
8347   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8348   PetscErrorCode  ierr;
8349 
8350   PetscFunctionBegin;
8351   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8352   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8353   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8354   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8355   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8356   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8357   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8358   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8359   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8360   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8361   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
8362   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8363   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8364   PetscCall(PetscArrayzero(foffsets, 32));
8365   PetscCall(PetscArrayzero(coffsets, 32));
8366   /* Column indices */
8367   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8368   maxFPoints = numCPoints;
8369   /* Compress out points not in the section */
8370   /*   TODO: Squeeze out points with 0 dof as well */
8371   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8372   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8373     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8374       cpoints[q * 2]     = cpoints[p];
8375       cpoints[q * 2 + 1] = cpoints[p + 1];
8376       ++q;
8377     }
8378   }
8379   numCPoints = q;
8380   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8381     PetscInt fdof;
8382 
8383     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8384     if (!dof) continue;
8385     for (f = 0; f < numFields; ++f) {
8386       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8387       coffsets[f + 1] += fdof;
8388     }
8389     numCIndices += dof;
8390   }
8391   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8392   /* Row indices */
8393   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8394   {
8395     DMPlexTransform tr;
8396     DMPolytopeType *rct;
8397     PetscInt       *rsize, *rcone, *rornt, Nt;
8398 
8399     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8400     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8401     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8402     numSubcells = rsize[Nt - 1];
8403     PetscCall(DMPlexTransformDestroy(&tr));
8404   }
8405   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8406   for (r = 0, q = 0; r < numSubcells; ++r) {
8407     /* TODO Map from coarse to fine cells */
8408     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8409     /* Compress out points not in the section */
8410     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8411     for (p = 0; p < numFPoints * 2; p += 2) {
8412       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8413         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8414         if (!dof) continue;
8415         for (s = 0; s < q; ++s)
8416           if (fpoints[p] == ftotpoints[s * 2]) break;
8417         if (s < q) continue;
8418         ftotpoints[q * 2]     = fpoints[p];
8419         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8420         ++q;
8421       }
8422     }
8423     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8424   }
8425   numFPoints = q;
8426   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8427     PetscInt fdof;
8428 
8429     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8430     if (!dof) continue;
8431     for (f = 0; f < numFields; ++f) {
8432       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8433       foffsets[f + 1] += fdof;
8434     }
8435     numFIndices += dof;
8436   }
8437   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8438 
8439   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8440   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8441   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8442   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8443   if (numFields) {
8444     const PetscInt **permsF[32] = {NULL};
8445     const PetscInt **permsC[32] = {NULL};
8446 
8447     for (f = 0; f < numFields; f++) {
8448       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8449       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8450     }
8451     for (p = 0; p < numFPoints; p++) {
8452       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8453       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8454     }
8455     for (p = 0; p < numCPoints; p++) {
8456       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8457       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8458     }
8459     for (f = 0; f < numFields; f++) {
8460       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8461       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8462     }
8463   } else {
8464     const PetscInt **permsF = NULL;
8465     const PetscInt **permsC = NULL;
8466 
8467     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8468     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8469     for (p = 0, off = 0; p < numFPoints; p++) {
8470       const PetscInt *perm = permsF ? permsF[p] : NULL;
8471 
8472       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8473       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8474     }
8475     for (p = 0, off = 0; p < numCPoints; p++) {
8476       const PetscInt *perm = permsC ? permsC[p] : NULL;
8477 
8478       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8479       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8480     }
8481     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8482     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8483   }
8484   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8485   /* TODO: flips */
8486   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8487   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8488   if (ierr) {
8489     PetscMPIInt rank;
8490 
8491     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8492     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8493     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8494     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8495     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8496   }
8497   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8498   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8499   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8500   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8501   PetscFunctionReturn(PETSC_SUCCESS);
8502 }
8503 
8504 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8505 {
8506   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8507   PetscInt       *cpoints      = NULL;
8508   PetscInt        foffsets[32] = {0}, coffsets[32] = {0};
8509   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8510   DMPolytopeType  ct;
8511   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8512 
8513   PetscFunctionBegin;
8514   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8515   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8516   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8517   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8518   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8519   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8520   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8521   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8522   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8523   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8524   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8525   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8526   /* Column indices */
8527   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8528   maxFPoints = numCPoints;
8529   /* Compress out points not in the section */
8530   /*   TODO: Squeeze out points with 0 dof as well */
8531   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8532   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8533     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8534       cpoints[q * 2]     = cpoints[p];
8535       cpoints[q * 2 + 1] = cpoints[p + 1];
8536       ++q;
8537     }
8538   }
8539   numCPoints = q;
8540   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8541     PetscInt fdof;
8542 
8543     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8544     if (!dof) continue;
8545     for (f = 0; f < numFields; ++f) {
8546       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8547       coffsets[f + 1] += fdof;
8548     }
8549     numCIndices += dof;
8550   }
8551   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8552   /* Row indices */
8553   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8554   {
8555     DMPlexTransform tr;
8556     DMPolytopeType *rct;
8557     PetscInt       *rsize, *rcone, *rornt, Nt;
8558 
8559     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8560     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8561     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8562     numSubcells = rsize[Nt - 1];
8563     PetscCall(DMPlexTransformDestroy(&tr));
8564   }
8565   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8566   for (r = 0, q = 0; r < numSubcells; ++r) {
8567     /* TODO Map from coarse to fine cells */
8568     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8569     /* Compress out points not in the section */
8570     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8571     for (p = 0; p < numFPoints * 2; p += 2) {
8572       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8573         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8574         if (!dof) continue;
8575         for (s = 0; s < q; ++s)
8576           if (fpoints[p] == ftotpoints[s * 2]) break;
8577         if (s < q) continue;
8578         ftotpoints[q * 2]     = fpoints[p];
8579         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8580         ++q;
8581       }
8582     }
8583     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8584   }
8585   numFPoints = q;
8586   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8587     PetscInt fdof;
8588 
8589     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8590     if (!dof) continue;
8591     for (f = 0; f < numFields; ++f) {
8592       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8593       foffsets[f + 1] += fdof;
8594     }
8595     numFIndices += dof;
8596   }
8597   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8598 
8599   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8600   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8601   if (numFields) {
8602     const PetscInt **permsF[32] = {NULL};
8603     const PetscInt **permsC[32] = {NULL};
8604 
8605     for (f = 0; f < numFields; f++) {
8606       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8607       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8608     }
8609     for (p = 0; p < numFPoints; p++) {
8610       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8611       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8612     }
8613     for (p = 0; p < numCPoints; p++) {
8614       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8615       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8616     }
8617     for (f = 0; f < numFields; f++) {
8618       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8619       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8620     }
8621   } else {
8622     const PetscInt **permsF = NULL;
8623     const PetscInt **permsC = NULL;
8624 
8625     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8626     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8627     for (p = 0, off = 0; p < numFPoints; p++) {
8628       const PetscInt *perm = permsF ? permsF[p] : NULL;
8629 
8630       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8631       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8632     }
8633     for (p = 0, off = 0; p < numCPoints; p++) {
8634       const PetscInt *perm = permsC ? permsC[p] : NULL;
8635 
8636       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8637       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8638     }
8639     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8640     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8641   }
8642   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8643   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8644   PetscFunctionReturn(PETSC_SUCCESS);
8645 }
8646 
8647 /*@
8648   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8649 
8650   Input Parameter:
8651 . dm - The `DMPLEX` object
8652 
8653   Output Parameter:
8654 . cellHeight - The height of a cell
8655 
8656   Level: developer
8657 
8658 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetVTKCellHeight()`
8659 @*/
8660 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8661 {
8662   DM_Plex *mesh = (DM_Plex *)dm->data;
8663 
8664   PetscFunctionBegin;
8665   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8666   PetscAssertPointer(cellHeight, 2);
8667   *cellHeight = mesh->vtkCellHeight;
8668   PetscFunctionReturn(PETSC_SUCCESS);
8669 }
8670 
8671 /*@
8672   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8673 
8674   Input Parameters:
8675 + dm         - The `DMPLEX` object
8676 - cellHeight - The height of a cell
8677 
8678   Level: developer
8679 
8680 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8681 @*/
8682 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8683 {
8684   DM_Plex *mesh = (DM_Plex *)dm->data;
8685 
8686   PetscFunctionBegin;
8687   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8688   mesh->vtkCellHeight = cellHeight;
8689   PetscFunctionReturn(PETSC_SUCCESS);
8690 }
8691 
8692 /*@
8693   DMPlexGetCellTypeStratum - Get the range of cells of a given celltype
8694 
8695   Input Parameters:
8696 + dm - The `DMPLEX` object
8697 - ct - The `DMPolytopeType` of the cell
8698 
8699   Output Parameters:
8700 + start - The first cell of this type, or `NULL`
8701 - end   - The upper bound on this celltype, or `NULL`
8702 
8703   Level: advanced
8704 
8705 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8706 @*/
8707 PetscErrorCode DMPlexGetCellTypeStratum(DM dm, DMPolytopeType ct, PetscInt *start, PetscInt *end)
8708 {
8709   DM_Plex *mesh = (DM_Plex *)dm->data;
8710   DMLabel  label;
8711   PetscInt pStart, pEnd;
8712 
8713   PetscFunctionBegin;
8714   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8715   if (start) {
8716     PetscAssertPointer(start, 3);
8717     *start = 0;
8718   }
8719   if (end) {
8720     PetscAssertPointer(end, 4);
8721     *end = 0;
8722   }
8723   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8724   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
8725   if (mesh->tr) {
8726     PetscCall(DMPlexTransformGetCellTypeStratum(mesh->tr, ct, start, end));
8727   } else {
8728     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
8729     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named celltype was found");
8730     PetscCall(DMLabelGetStratumBounds(label, ct, start, end));
8731   }
8732   PetscFunctionReturn(PETSC_SUCCESS);
8733 }
8734 
8735 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8736 {
8737   PetscSection section, globalSection;
8738   PetscInt    *numbers, p;
8739 
8740   PetscFunctionBegin;
8741   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8742   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8743   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8744   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8745   PetscCall(PetscSectionSetUp(section));
8746   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &globalSection));
8747   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8748   for (p = pStart; p < pEnd; ++p) {
8749     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8750     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8751     else numbers[p - pStart] += shift;
8752   }
8753   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8754   if (globalSize) {
8755     PetscLayout layout;
8756     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8757     PetscCall(PetscLayoutGetSize(layout, globalSize));
8758     PetscCall(PetscLayoutDestroy(&layout));
8759   }
8760   PetscCall(PetscSectionDestroy(&section));
8761   PetscCall(PetscSectionDestroy(&globalSection));
8762   PetscFunctionReturn(PETSC_SUCCESS);
8763 }
8764 
8765 /*@
8766   DMPlexCreateCellNumbering - Get a global cell numbering for all cells on this process
8767 
8768   Input Parameters:
8769 + dm         - The `DMPLEX` object
8770 - includeAll - Whether to include all cells, or just the simplex and box cells
8771 
8772   Output Parameter:
8773 . globalCellNumbers - Global cell numbers for all cells on this process
8774 
8775   Level: developer
8776 
8777 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`
8778 @*/
8779 PetscErrorCode DMPlexCreateCellNumbering(DM dm, PetscBool includeAll, IS *globalCellNumbers)
8780 {
8781   PetscInt cellHeight, cStart, cEnd;
8782 
8783   PetscFunctionBegin;
8784   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8785   if (includeAll) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8786   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8787   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8788   PetscFunctionReturn(PETSC_SUCCESS);
8789 }
8790 
8791 /*@
8792   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8793 
8794   Input Parameter:
8795 . dm - The `DMPLEX` object
8796 
8797   Output Parameter:
8798 . globalCellNumbers - Global cell numbers for all cells on this process
8799 
8800   Level: developer
8801 
8802 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateCellNumbering()`, `DMPlexGetVertexNumbering()`
8803 @*/
8804 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8805 {
8806   DM_Plex *mesh = (DM_Plex *)dm->data;
8807 
8808   PetscFunctionBegin;
8809   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8810   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8811   *globalCellNumbers = mesh->globalCellNumbers;
8812   PetscFunctionReturn(PETSC_SUCCESS);
8813 }
8814 
8815 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8816 {
8817   PetscInt vStart, vEnd;
8818 
8819   PetscFunctionBegin;
8820   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8821   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8822   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8823   PetscFunctionReturn(PETSC_SUCCESS);
8824 }
8825 
8826 /*@
8827   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8828 
8829   Input Parameter:
8830 . dm - The `DMPLEX` object
8831 
8832   Output Parameter:
8833 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8834 
8835   Level: developer
8836 
8837 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8838 @*/
8839 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8840 {
8841   DM_Plex *mesh = (DM_Plex *)dm->data;
8842 
8843   PetscFunctionBegin;
8844   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8845   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8846   *globalVertexNumbers = mesh->globalVertexNumbers;
8847   PetscFunctionReturn(PETSC_SUCCESS);
8848 }
8849 
8850 /*@
8851   DMPlexCreatePointNumbering - Create a global numbering for all points.
8852 
8853   Collective
8854 
8855   Input Parameter:
8856 . dm - The `DMPLEX` object
8857 
8858   Output Parameter:
8859 . globalPointNumbers - Global numbers for all points on this process
8860 
8861   Level: developer
8862 
8863   Notes:
8864   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8865   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8866   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8867   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8868 
8869   The partitioned mesh is
8870   ```
8871   (2)--0--(3)--1--(4)    (1)--0--(2)
8872   ```
8873   and its global numbering is
8874   ```
8875   (3)--0--(4)--1--(5)--2--(6)
8876   ```
8877   Then the global numbering is provided as
8878   ```
8879   [0] Number of indices in set 5
8880   [0] 0 0
8881   [0] 1 1
8882   [0] 2 3
8883   [0] 3 4
8884   [0] 4 -6
8885   [1] Number of indices in set 3
8886   [1] 0 2
8887   [1] 1 5
8888   [1] 2 6
8889   ```
8890 
8891 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8892 @*/
8893 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8894 {
8895   IS        nums[4];
8896   PetscInt  depths[4], gdepths[4], starts[4];
8897   PetscInt  depth, d, shift = 0;
8898   PetscBool empty = PETSC_FALSE;
8899 
8900   PetscFunctionBegin;
8901   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8902   PetscCall(DMPlexGetDepth(dm, &depth));
8903   // For unstratified meshes use dim instead of depth
8904   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8905   // If any stratum is empty, we must mark all empty
8906   for (d = 0; d <= depth; ++d) {
8907     PetscInt end;
8908 
8909     depths[d] = depth - d;
8910     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8911     if (!(starts[d] - end)) empty = PETSC_TRUE;
8912   }
8913   if (empty)
8914     for (d = 0; d <= depth; ++d) {
8915       depths[d] = -1;
8916       starts[d] = -1;
8917     }
8918   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8919   PetscCall(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8920   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]);
8921   // Note here that 'shift' is collective, so that the numbering is stratified by depth
8922   for (d = 0; d <= depth; ++d) {
8923     PetscInt pStart, pEnd, gsize;
8924 
8925     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8926     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8927     shift += gsize;
8928   }
8929   PetscCall(ISConcatenate(PETSC_COMM_SELF, depth + 1, nums, globalPointNumbers));
8930   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8931   PetscFunctionReturn(PETSC_SUCCESS);
8932 }
8933 
8934 /*@
8935   DMPlexCreateEdgeNumbering - Create a global numbering for edges.
8936 
8937   Collective
8938 
8939   Input Parameter:
8940 . dm - The `DMPLEX` object
8941 
8942   Output Parameter:
8943 . globalEdgeNumbers - Global numbers for all edges on this process
8944 
8945   Level: developer
8946 
8947   Notes:
8948   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).
8949 
8950 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`, `DMPlexCreatePointNumbering()`
8951 @*/
8952 PetscErrorCode DMPlexCreateEdgeNumbering(DM dm, IS *globalEdgeNumbers)
8953 {
8954   PetscSF  sf;
8955   PetscInt eStart, eEnd;
8956 
8957   PetscFunctionBegin;
8958   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8959   PetscCall(DMGetPointSF(dm, &sf));
8960   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
8961   PetscCall(DMPlexCreateNumbering_Plex(dm, eStart, eEnd, 0, NULL, sf, globalEdgeNumbers));
8962   PetscFunctionReturn(PETSC_SUCCESS);
8963 }
8964 
8965 /*@
8966   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8967 
8968   Input Parameter:
8969 . dm - The `DMPLEX` object
8970 
8971   Output Parameter:
8972 . ranks - The rank field
8973 
8974   Options Database Key:
8975 . -dm_partition_view - Adds the rank field into the `DM` output from `-dm_view` using the same viewer
8976 
8977   Level: intermediate
8978 
8979 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8980 @*/
8981 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8982 {
8983   DM             rdm;
8984   PetscFE        fe;
8985   PetscScalar   *r;
8986   PetscMPIInt    rank;
8987   DMPolytopeType ct;
8988   PetscInt       dim, cStart, cEnd, c;
8989   PetscBool      simplex;
8990 
8991   PetscFunctionBeginUser;
8992   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8993   PetscAssertPointer(ranks, 2);
8994   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
8995   PetscCall(DMClone(dm, &rdm));
8996   PetscCall(DMGetDimension(rdm, &dim));
8997   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8998   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8999   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
9000   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
9001   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
9002   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
9003   PetscCall(PetscFEDestroy(&fe));
9004   PetscCall(DMCreateDS(rdm));
9005   PetscCall(DMCreateGlobalVector(rdm, ranks));
9006   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
9007   PetscCall(VecGetArray(*ranks, &r));
9008   for (c = cStart; c < cEnd; ++c) {
9009     PetscScalar *lr;
9010 
9011     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
9012     if (lr) *lr = rank;
9013   }
9014   PetscCall(VecRestoreArray(*ranks, &r));
9015   PetscCall(DMDestroy(&rdm));
9016   PetscFunctionReturn(PETSC_SUCCESS);
9017 }
9018 
9019 /*@
9020   DMPlexCreateLabelField - Create a field whose value is the label value for that point
9021 
9022   Input Parameters:
9023 + dm    - The `DMPLEX`
9024 - label - The `DMLabel`
9025 
9026   Output Parameter:
9027 . val - The label value field
9028 
9029   Options Database Key:
9030 . -dm_label_view - Adds the label value field into the `DM` output from `-dm_view` using the same viewer
9031 
9032   Level: intermediate
9033 
9034 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
9035 @*/
9036 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
9037 {
9038   DM             rdm, plex;
9039   Vec            lval;
9040   PetscSection   section;
9041   PetscFE        fe;
9042   PetscScalar   *v;
9043   PetscInt       dim, pStart, pEnd, p, cStart;
9044   DMPolytopeType ct;
9045   char           name[PETSC_MAX_PATH_LEN];
9046   const char    *lname, *prefix;
9047 
9048   PetscFunctionBeginUser;
9049   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9050   PetscAssertPointer(label, 2);
9051   PetscAssertPointer(val, 3);
9052   PetscCall(DMClone(dm, &rdm));
9053   PetscCall(DMConvert(rdm, DMPLEX, &plex));
9054   PetscCall(DMPlexGetHeightStratum(plex, 0, &cStart, NULL));
9055   PetscCall(DMPlexGetCellType(plex, cStart, &ct));
9056   PetscCall(DMDestroy(&plex));
9057   PetscCall(DMGetDimension(rdm, &dim));
9058   PetscCall(DMGetOptionsPrefix(dm, &prefix));
9059   PetscCall(PetscObjectGetName((PetscObject)label, &lname));
9060   PetscCall(PetscSNPrintf(name, sizeof(name), "%s%s_", prefix ? prefix : "", lname));
9061   PetscCall(PetscFECreateByCell(PETSC_COMM_SELF, dim, 1, ct, name, -1, &fe));
9062   PetscCall(PetscObjectSetName((PetscObject)fe, ""));
9063   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
9064   PetscCall(PetscFEDestroy(&fe));
9065   PetscCall(DMCreateDS(rdm));
9066   PetscCall(DMCreateGlobalVector(rdm, val));
9067   PetscCall(DMCreateLocalVector(rdm, &lval));
9068   PetscCall(PetscObjectSetName((PetscObject)*val, lname));
9069   PetscCall(DMGetLocalSection(rdm, &section));
9070   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
9071   PetscCall(VecGetArray(lval, &v));
9072   for (p = pStart; p < pEnd; ++p) {
9073     PetscInt cval, dof, off;
9074 
9075     PetscCall(PetscSectionGetDof(section, p, &dof));
9076     if (!dof) continue;
9077     PetscCall(DMLabelGetValue(label, p, &cval));
9078     PetscCall(PetscSectionGetOffset(section, p, &off));
9079     for (PetscInt d = 0; d < dof; d++) v[off + d] = cval;
9080   }
9081   PetscCall(VecRestoreArray(lval, &v));
9082   PetscCall(DMLocalToGlobal(rdm, lval, INSERT_VALUES, *val));
9083   PetscCall(VecDestroy(&lval));
9084   PetscCall(DMDestroy(&rdm));
9085   PetscFunctionReturn(PETSC_SUCCESS);
9086 }
9087 
9088 /*@
9089   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
9090 
9091   Input Parameter:
9092 . dm - The `DMPLEX` object
9093 
9094   Level: developer
9095 
9096   Notes:
9097   This is a useful diagnostic when creating meshes programmatically.
9098 
9099   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9100 
9101 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9102 @*/
9103 PetscErrorCode DMPlexCheckSymmetry(DM dm)
9104 {
9105   PetscSection    coneSection, supportSection;
9106   const PetscInt *cone, *support;
9107   PetscInt        coneSize, c, supportSize, s;
9108   PetscInt        pStart, pEnd, p, pp, csize, ssize;
9109   PetscBool       storagecheck = PETSC_TRUE;
9110 
9111   PetscFunctionBegin;
9112   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9113   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
9114   PetscCall(DMPlexGetConeSection(dm, &coneSection));
9115   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
9116   /* Check that point p is found in the support of its cone points, and vice versa */
9117   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9118   for (p = pStart; p < pEnd; ++p) {
9119     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
9120     PetscCall(DMPlexGetCone(dm, p, &cone));
9121     for (c = 0; c < coneSize; ++c) {
9122       PetscBool dup = PETSC_FALSE;
9123       PetscInt  d;
9124       for (d = c - 1; d >= 0; --d) {
9125         if (cone[c] == cone[d]) {
9126           dup = PETSC_TRUE;
9127           break;
9128         }
9129       }
9130       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
9131       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
9132       for (s = 0; s < supportSize; ++s) {
9133         if (support[s] == p) break;
9134       }
9135       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
9136         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
9137         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
9138         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9139         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
9140         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
9141         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9142         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]);
9143         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
9144       }
9145     }
9146     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
9147     if (p != pp) {
9148       storagecheck = PETSC_FALSE;
9149       continue;
9150     }
9151     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
9152     PetscCall(DMPlexGetSupport(dm, p, &support));
9153     for (s = 0; s < supportSize; ++s) {
9154       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
9155       PetscCall(DMPlexGetCone(dm, support[s], &cone));
9156       for (c = 0; c < coneSize; ++c) {
9157         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
9158         if (cone[c] != pp) {
9159           c = 0;
9160           break;
9161         }
9162         if (cone[c] == p) break;
9163       }
9164       if (c >= coneSize) {
9165         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
9166         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
9167         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9168         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
9169         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
9170         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9171         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
9172       }
9173     }
9174   }
9175   if (storagecheck) {
9176     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
9177     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
9178     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
9179   }
9180   PetscFunctionReturn(PETSC_SUCCESS);
9181 }
9182 
9183 /*
9184   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.
9185 */
9186 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
9187 {
9188   DMPolytopeType  cct;
9189   PetscInt        ptpoints[4];
9190   const PetscInt *cone, *ccone, *ptcone;
9191   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
9192 
9193   PetscFunctionBegin;
9194   *unsplit = 0;
9195   switch (ct) {
9196   case DM_POLYTOPE_POINT_PRISM_TENSOR:
9197     ptpoints[npt++] = c;
9198     break;
9199   case DM_POLYTOPE_SEG_PRISM_TENSOR:
9200     PetscCall(DMPlexGetCone(dm, c, &cone));
9201     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9202     for (cp = 0; cp < coneSize; ++cp) {
9203       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
9204       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
9205     }
9206     break;
9207   case DM_POLYTOPE_TRI_PRISM_TENSOR:
9208   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9209     PetscCall(DMPlexGetCone(dm, c, &cone));
9210     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9211     for (cp = 0; cp < coneSize; ++cp) {
9212       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
9213       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
9214       for (ccp = 0; ccp < cconeSize; ++ccp) {
9215         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
9216         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
9217           PetscInt p;
9218           for (p = 0; p < npt; ++p)
9219             if (ptpoints[p] == ccone[ccp]) break;
9220           if (p == npt) ptpoints[npt++] = ccone[ccp];
9221         }
9222       }
9223     }
9224     break;
9225   default:
9226     break;
9227   }
9228   for (pt = 0; pt < npt; ++pt) {
9229     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
9230     if (ptcone[0] == ptcone[1]) ++(*unsplit);
9231   }
9232   PetscFunctionReturn(PETSC_SUCCESS);
9233 }
9234 
9235 /*@
9236   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
9237 
9238   Input Parameters:
9239 + dm         - The `DMPLEX` object
9240 - cellHeight - Normally 0
9241 
9242   Level: developer
9243 
9244   Notes:
9245   This is a useful diagnostic when creating meshes programmatically.
9246   Currently applicable only to homogeneous simplex or tensor meshes.
9247 
9248   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9249 
9250 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9251 @*/
9252 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
9253 {
9254   DMPlexInterpolatedFlag interp;
9255   DMPolytopeType         ct;
9256   PetscInt               vStart, vEnd, cStart, cEnd, c;
9257 
9258   PetscFunctionBegin;
9259   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9260   PetscCall(DMPlexIsInterpolated(dm, &interp));
9261   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9262   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9263   for (c = cStart; c < cEnd; ++c) {
9264     PetscInt *closure = NULL;
9265     PetscInt  coneSize, closureSize, cl, Nv = 0;
9266 
9267     PetscCall(DMPlexGetCellType(dm, c, &ct));
9268     if (ct == DM_POLYTOPE_UNKNOWN) continue;
9269     if (interp == DMPLEX_INTERPOLATED_FULL) {
9270       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9271       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));
9272     }
9273     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9274     for (cl = 0; cl < closureSize * 2; cl += 2) {
9275       const PetscInt p = closure[cl];
9276       if ((p >= vStart) && (p < vEnd)) ++Nv;
9277     }
9278     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9279     /* Special Case: Tensor faces with identified vertices */
9280     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
9281       PetscInt unsplit;
9282 
9283       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9284       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
9285     }
9286     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));
9287   }
9288   PetscFunctionReturn(PETSC_SUCCESS);
9289 }
9290 
9291 /*@
9292   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
9293 
9294   Collective
9295 
9296   Input Parameters:
9297 + dm         - The `DMPLEX` object
9298 - cellHeight - Normally 0
9299 
9300   Level: developer
9301 
9302   Notes:
9303   This is a useful diagnostic when creating meshes programmatically.
9304   This routine is only relevant for meshes that are fully interpolated across all ranks.
9305   It will error out if a partially interpolated mesh is given on some rank.
9306   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
9307 
9308   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9309 
9310 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
9311 @*/
9312 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
9313 {
9314   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
9315   DMPlexInterpolatedFlag interpEnum;
9316 
9317   PetscFunctionBegin;
9318   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9319   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
9320   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(PETSC_SUCCESS);
9321   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
9322     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported"));
9323     PetscFunctionReturn(PETSC_SUCCESS);
9324   }
9325 
9326   PetscCall(DMGetDimension(dm, &dim));
9327   PetscCall(DMPlexGetDepth(dm, &depth));
9328   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9329   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
9330     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
9331     for (c = cStart; c < cEnd; ++c) {
9332       const PetscInt       *cone, *ornt, *faceSizes, *faces;
9333       const DMPolytopeType *faceTypes;
9334       DMPolytopeType        ct;
9335       PetscInt              numFaces, coneSize, f;
9336       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
9337 
9338       PetscCall(DMPlexGetCellType(dm, c, &ct));
9339       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9340       if (unsplit) continue;
9341       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9342       PetscCall(DMPlexGetCone(dm, c, &cone));
9343       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
9344       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9345       for (cl = 0; cl < closureSize * 2; cl += 2) {
9346         const PetscInt p = closure[cl];
9347         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
9348       }
9349       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9350       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);
9351       for (f = 0; f < numFaces; ++f) {
9352         DMPolytopeType fct;
9353         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
9354 
9355         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
9356         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
9357         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
9358           const PetscInt p = fclosure[cl];
9359           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
9360         }
9361         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]);
9362         for (v = 0; v < fnumCorners; ++v) {
9363           if (fclosure[v] != faces[fOff + v]) {
9364             PetscInt v1;
9365 
9366             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
9367             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
9368             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
9369             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
9370             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9371             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]);
9372           }
9373         }
9374         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
9375         fOff += faceSizes[f];
9376       }
9377       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9378       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9379     }
9380   }
9381   PetscFunctionReturn(PETSC_SUCCESS);
9382 }
9383 
9384 /*@
9385   DMPlexCheckGeometry - Check the geometry of mesh cells
9386 
9387   Input Parameter:
9388 . dm - The `DMPLEX` object
9389 
9390   Level: developer
9391 
9392   Notes:
9393   This is a useful diagnostic when creating meshes programmatically.
9394 
9395   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9396 
9397 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9398 @*/
9399 PetscErrorCode DMPlexCheckGeometry(DM dm)
9400 {
9401   Vec       coordinates;
9402   PetscReal detJ, J[9], refVol = 1.0;
9403   PetscReal vol;
9404   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
9405 
9406   PetscFunctionBegin;
9407   PetscCall(DMGetDimension(dm, &dim));
9408   PetscCall(DMGetCoordinateDim(dm, &dE));
9409   if (dim != dE) PetscFunctionReturn(PETSC_SUCCESS);
9410   PetscCall(DMPlexGetDepth(dm, &depth));
9411   for (d = 0; d < dim; ++d) refVol *= 2.0;
9412   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9413   /* Make sure local coordinates are created, because that step is collective */
9414   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
9415   if (!coordinates) PetscFunctionReturn(PETSC_SUCCESS);
9416   for (c = cStart; c < cEnd; ++c) {
9417     DMPolytopeType ct;
9418     PetscInt       unsplit;
9419     PetscBool      ignoreZeroVol = PETSC_FALSE;
9420 
9421     PetscCall(DMPlexGetCellType(dm, c, &ct));
9422     switch (ct) {
9423     case DM_POLYTOPE_SEG_PRISM_TENSOR:
9424     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9425     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9426       ignoreZeroVol = PETSC_TRUE;
9427       break;
9428     default:
9429       break;
9430     }
9431     switch (ct) {
9432     case DM_POLYTOPE_TRI_PRISM:
9433     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9434     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9435     case DM_POLYTOPE_PYRAMID:
9436       continue;
9437     default:
9438       break;
9439     }
9440     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9441     if (unsplit) continue;
9442     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
9443     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);
9444     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
9445     /* This should work with periodicity since DG coordinates should be used */
9446     if (depth > 1) {
9447       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
9448       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);
9449       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
9450     }
9451   }
9452   PetscFunctionReturn(PETSC_SUCCESS);
9453 }
9454 
9455 /*@
9456   DMPlexCheckPointSF - Check that several necessary conditions are met for the point `PetscSF` of this plex.
9457 
9458   Collective
9459 
9460   Input Parameters:
9461 + dm              - The `DMPLEX` object
9462 . pointSF         - The `PetscSF`, or `NULL` for `PointSF` attached to `DM`
9463 - allowExtraRoots - Flag to allow extra points not present in the `DM`
9464 
9465   Level: developer
9466 
9467   Notes:
9468   This is mainly intended for debugging/testing purposes.
9469 
9470   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9471 
9472   Extra roots can come from periodic cuts, where additional points appear on the boundary
9473 
9474 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
9475 @*/
9476 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
9477 {
9478   PetscInt           l, nleaves, nroots, overlap;
9479   const PetscInt    *locals;
9480   const PetscSFNode *remotes;
9481   PetscBool          distributed;
9482   MPI_Comm           comm;
9483   PetscMPIInt        rank;
9484 
9485   PetscFunctionBegin;
9486   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9487   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
9488   else pointSF = dm->sf;
9489   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9490   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
9491   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9492   {
9493     PetscMPIInt mpiFlag;
9494 
9495     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
9496     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
9497   }
9498   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
9499   PetscCall(DMPlexIsDistributed(dm, &distributed));
9500   if (!distributed) {
9501     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);
9502     PetscFunctionReturn(PETSC_SUCCESS);
9503   }
9504   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);
9505   PetscCall(DMPlexGetOverlap(dm, &overlap));
9506 
9507   /* Check SF graph is compatible with DMPlex chart */
9508   {
9509     PetscInt pStart, pEnd, maxLeaf;
9510 
9511     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9512     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9513     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9514     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9515   }
9516 
9517   /* Check Point SF has no local points referenced */
9518   for (l = 0; l < nleaves; l++) {
9519     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);
9520   }
9521 
9522   /* Check there are no cells in interface */
9523   if (!overlap) {
9524     PetscInt cellHeight, cStart, cEnd;
9525 
9526     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9527     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9528     for (l = 0; l < nleaves; ++l) {
9529       const PetscInt point = locals ? locals[l] : l;
9530 
9531       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9532     }
9533   }
9534 
9535   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9536   {
9537     const PetscInt *rootdegree;
9538 
9539     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9540     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9541     for (l = 0; l < nleaves; ++l) {
9542       const PetscInt  point = locals ? locals[l] : l;
9543       const PetscInt *cone;
9544       PetscInt        coneSize, c, idx;
9545 
9546       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9547       PetscCall(DMPlexGetCone(dm, point, &cone));
9548       for (c = 0; c < coneSize; ++c) {
9549         if (!rootdegree[cone[c]]) {
9550           if (locals) {
9551             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9552           } else {
9553             idx = (cone[c] < nleaves) ? cone[c] : -1;
9554           }
9555           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9556         }
9557       }
9558     }
9559   }
9560   PetscFunctionReturn(PETSC_SUCCESS);
9561 }
9562 
9563 /*@
9564   DMPlexCheckOrphanVertices - Check that no vertices are disconnected from the mesh, unless the mesh only consists of disconnected vertices.
9565 
9566   Collective
9567 
9568   Input Parameter:
9569 . dm - The `DMPLEX` object
9570 
9571   Level: developer
9572 
9573   Notes:
9574   This is mainly intended for debugging/testing purposes.
9575 
9576   Other cell types which are disconnected would be caught by the symmetry and face checks.
9577 
9578   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9579 
9580 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheck()`, `DMSetFromOptions()`
9581 @*/
9582 PetscErrorCode DMPlexCheckOrphanVertices(DM dm)
9583 {
9584   PetscInt pStart, pEnd, vStart, vEnd;
9585 
9586   PetscFunctionBegin;
9587   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9588   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9589   if (pStart == vStart && pEnd == vEnd) PetscFunctionReturn(PETSC_SUCCESS);
9590   for (PetscInt v = vStart; v < vEnd; ++v) {
9591     PetscInt suppSize;
9592 
9593     PetscCall(DMPlexGetSupportSize(dm, v, &suppSize));
9594     PetscCheck(suppSize, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Vertex %" PetscInt_FMT " is disconnected from the mesh", v);
9595   }
9596   PetscFunctionReturn(PETSC_SUCCESS);
9597 }
9598 
9599 /*@
9600   DMPlexCheck - Perform various checks of `DMPLEX` sanity
9601 
9602   Input Parameter:
9603 . dm - The `DMPLEX` object
9604 
9605   Level: developer
9606 
9607   Notes:
9608   This is a useful diagnostic when creating meshes programmatically.
9609 
9610   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9611 
9612   Currently does not include `DMPlexCheckCellShape()`.
9613 
9614 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9615 @*/
9616 PetscErrorCode DMPlexCheck(DM dm)
9617 {
9618   PetscInt cellHeight;
9619 
9620   PetscFunctionBegin;
9621   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9622   PetscCall(DMPlexCheckSymmetry(dm));
9623   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9624   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9625   PetscCall(DMPlexCheckGeometry(dm));
9626   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9627   PetscCall(DMPlexCheckInterfaceCones(dm));
9628   PetscCall(DMPlexCheckOrphanVertices(dm));
9629   PetscFunctionReturn(PETSC_SUCCESS);
9630 }
9631 
9632 typedef struct cell_stats {
9633   PetscReal min, max, sum, squaresum;
9634   PetscInt  count;
9635 } cell_stats_t;
9636 
9637 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9638 {
9639   PetscInt i, N = *len;
9640 
9641   for (i = 0; i < N; i++) {
9642     cell_stats_t *A = (cell_stats_t *)a;
9643     cell_stats_t *B = (cell_stats_t *)b;
9644 
9645     B->min = PetscMin(A->min, B->min);
9646     B->max = PetscMax(A->max, B->max);
9647     B->sum += A->sum;
9648     B->squaresum += A->squaresum;
9649     B->count += A->count;
9650   }
9651 }
9652 
9653 /*@
9654   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9655 
9656   Collective
9657 
9658   Input Parameters:
9659 + dm        - The `DMPLEX` object
9660 . output    - If true, statistics will be displayed on `stdout`
9661 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9662 
9663   Level: developer
9664 
9665   Notes:
9666   This is mainly intended for debugging/testing purposes.
9667 
9668   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9669 
9670 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9671 @*/
9672 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9673 {
9674   DM           dmCoarse;
9675   cell_stats_t stats, globalStats;
9676   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9677   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9678   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9679   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9680   PetscMPIInt  rank, size;
9681 
9682   PetscFunctionBegin;
9683   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9684   stats.min = PETSC_MAX_REAL;
9685   stats.max = PETSC_MIN_REAL;
9686   stats.sum = stats.squaresum = 0.;
9687   stats.count                 = 0;
9688 
9689   PetscCallMPI(MPI_Comm_size(comm, &size));
9690   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9691   PetscCall(DMGetCoordinateDim(dm, &cdim));
9692   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9693   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9694   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9695   for (c = cStart; c < cEnd; c++) {
9696     PetscInt  i;
9697     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9698 
9699     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9700     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9701     for (i = 0; i < PetscSqr(cdim); ++i) {
9702       frobJ += J[i] * J[i];
9703       frobInvJ += invJ[i] * invJ[i];
9704     }
9705     cond2 = frobJ * frobInvJ;
9706     cond  = PetscSqrtReal(cond2);
9707 
9708     stats.min = PetscMin(stats.min, cond);
9709     stats.max = PetscMax(stats.max, cond);
9710     stats.sum += cond;
9711     stats.squaresum += cond2;
9712     stats.count++;
9713     if (output && cond > limit) {
9714       PetscSection coordSection;
9715       Vec          coordsLocal;
9716       PetscScalar *coords = NULL;
9717       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9718 
9719       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9720       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9721       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9722       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9723       for (i = 0; i < Nv / cdim; ++i) {
9724         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9725         for (d = 0; d < cdim; ++d) {
9726           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9727           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9728         }
9729         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9730       }
9731       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9732       for (cl = 0; cl < clSize * 2; cl += 2) {
9733         const PetscInt edge = closure[cl];
9734 
9735         if ((edge >= eStart) && (edge < eEnd)) {
9736           PetscReal len;
9737 
9738           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9739           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9740         }
9741       }
9742       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9743       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9744     }
9745   }
9746   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9747 
9748   if (size > 1) {
9749     PetscMPIInt  blockLengths[2] = {4, 1};
9750     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9751     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9752     MPI_Op       statReduce;
9753 
9754     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9755     PetscCallMPI(MPI_Type_commit(&statType));
9756     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9757     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9758     PetscCallMPI(MPI_Op_free(&statReduce));
9759     PetscCallMPI(MPI_Type_free(&statType));
9760   } else {
9761     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9762   }
9763   if (rank == 0) {
9764     count = globalStats.count;
9765     min   = globalStats.min;
9766     max   = globalStats.max;
9767     mean  = globalStats.sum / globalStats.count;
9768     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9769   }
9770 
9771   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));
9772   PetscCall(PetscFree2(J, invJ));
9773 
9774   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9775   if (dmCoarse) {
9776     PetscBool isplex;
9777 
9778     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9779     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9780   }
9781   PetscFunctionReturn(PETSC_SUCCESS);
9782 }
9783 
9784 /*@
9785   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9786   orthogonal quality below given tolerance.
9787 
9788   Collective
9789 
9790   Input Parameters:
9791 + dm   - The `DMPLEX` object
9792 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9793 - atol - [0, 1] Absolute tolerance for tagging cells.
9794 
9795   Output Parameters:
9796 + OrthQual      - `Vec` containing orthogonal quality per cell
9797 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9798 
9799   Options Database Keys:
9800 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9801 - -dm_plex_orthogonal_quality_vec_view   - view OrthQual vector.
9802 
9803   Level: intermediate
9804 
9805   Notes:
9806   Orthogonal quality is given by the following formula\:
9807 
9808   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9809 
9810   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
9811   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9812   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9813   calculating the cosine of the angle between these vectors.
9814 
9815   Orthogonal quality ranges from 1 (best) to 0 (worst).
9816 
9817   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9818   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9819 
9820   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9821 
9822 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9823 @*/
9824 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9825 {
9826   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9827   PetscInt              *idx;
9828   PetscScalar           *oqVals;
9829   const PetscScalar     *cellGeomArr, *faceGeomArr;
9830   PetscReal             *ci, *fi, *Ai;
9831   MPI_Comm               comm;
9832   Vec                    cellgeom, facegeom;
9833   DM                     dmFace, dmCell;
9834   IS                     glob;
9835   ISLocalToGlobalMapping ltog;
9836   PetscViewer            vwr;
9837 
9838   PetscFunctionBegin;
9839   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9840   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9841   PetscAssertPointer(OrthQual, 4);
9842   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9843   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9844   PetscCall(DMGetDimension(dm, &nc));
9845   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9846   {
9847     DMPlexInterpolatedFlag interpFlag;
9848 
9849     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9850     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9851       PetscMPIInt rank;
9852 
9853       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9854       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9855     }
9856   }
9857   if (OrthQualLabel) {
9858     PetscAssertPointer(OrthQualLabel, 5);
9859     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9860     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9861   } else {
9862     *OrthQualLabel = NULL;
9863   }
9864   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9865   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9866   PetscCall(DMPlexCreateCellNumbering(dm, PETSC_TRUE, &glob));
9867   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9868   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9869   PetscCall(VecCreate(comm, OrthQual));
9870   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9871   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9872   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9873   PetscCall(VecSetUp(*OrthQual));
9874   PetscCall(ISDestroy(&glob));
9875   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9876   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9877   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9878   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9879   PetscCall(VecGetDM(cellgeom, &dmCell));
9880   PetscCall(VecGetDM(facegeom, &dmFace));
9881   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9882   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
9883     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9884     PetscInt         cellarr[2], *adj = NULL;
9885     PetscScalar     *cArr, *fArr;
9886     PetscReal        minvalc = 1.0, minvalf = 1.0;
9887     PetscFVCellGeom *cg;
9888 
9889     idx[cellIter] = cell - cStart;
9890     cellarr[0]    = cell;
9891     /* Make indexing into cellGeom easier */
9892     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9893     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9894     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9895     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9896     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
9897       PetscInt         i;
9898       const PetscInt   neigh  = adj[cellneigh];
9899       PetscReal        normci = 0, normfi = 0, normai = 0;
9900       PetscFVCellGeom *cgneigh;
9901       PetscFVFaceGeom *fg;
9902 
9903       /* Don't count ourselves in the neighbor list */
9904       if (neigh == cell) continue;
9905       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9906       cellarr[1] = neigh;
9907       {
9908         PetscInt        numcovpts;
9909         const PetscInt *covpts;
9910 
9911         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9912         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9913         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9914       }
9915 
9916       /* Compute c_i, f_i and their norms */
9917       for (i = 0; i < nc; i++) {
9918         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9919         fi[i] = fg->centroid[i] - cg->centroid[i];
9920         Ai[i] = fg->normal[i];
9921         normci += PetscPowReal(ci[i], 2);
9922         normfi += PetscPowReal(fi[i], 2);
9923         normai += PetscPowReal(Ai[i], 2);
9924       }
9925       normci = PetscSqrtReal(normci);
9926       normfi = PetscSqrtReal(normfi);
9927       normai = PetscSqrtReal(normai);
9928 
9929       /* Normalize and compute for each face-cell-normal pair */
9930       for (i = 0; i < nc; i++) {
9931         ci[i] = ci[i] / normci;
9932         fi[i] = fi[i] / normfi;
9933         Ai[i] = Ai[i] / normai;
9934         /* PetscAbs because I don't know if normals are guaranteed to point out */
9935         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
9936         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
9937       }
9938       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
9939       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
9940     }
9941     PetscCall(PetscFree(adj));
9942     PetscCall(PetscFree2(cArr, fArr));
9943     /* Defer to cell if they're equal */
9944     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9945     if (OrthQualLabel) {
9946       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9947     }
9948   }
9949   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
9950   PetscCall(VecAssemblyBegin(*OrthQual));
9951   PetscCall(VecAssemblyEnd(*OrthQual));
9952   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9953   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9954   PetscCall(PetscOptionsCreateViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9955   if (OrthQualLabel) {
9956     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9957   }
9958   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9959   PetscCall(PetscViewerDestroy(&vwr));
9960   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9961   PetscFunctionReturn(PETSC_SUCCESS);
9962 }
9963 
9964 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9965  * interpolator construction */
9966 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9967 {
9968   PetscSection section, newSection, gsection;
9969   PetscSF      sf;
9970   PetscBool    hasConstraints, ghasConstraints;
9971 
9972   PetscFunctionBegin;
9973   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9974   PetscAssertPointer(odm, 2);
9975   PetscCall(DMGetLocalSection(dm, &section));
9976   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9977   PetscCall(MPIU_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
9978   if (!ghasConstraints) {
9979     PetscCall(PetscObjectReference((PetscObject)dm));
9980     *odm = dm;
9981     PetscFunctionReturn(PETSC_SUCCESS);
9982   }
9983   PetscCall(DMClone(dm, odm));
9984   PetscCall(DMCopyFields(dm, *odm));
9985   PetscCall(DMGetLocalSection(*odm, &newSection));
9986   PetscCall(DMGetPointSF(*odm, &sf));
9987   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_TRUE, PETSC_FALSE, &gsection));
9988   PetscCall(DMSetGlobalSection(*odm, gsection));
9989   PetscCall(PetscSectionDestroy(&gsection));
9990   PetscFunctionReturn(PETSC_SUCCESS);
9991 }
9992 
9993 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9994 {
9995   DM        dmco, dmfo;
9996   Mat       interpo;
9997   Vec       rscale;
9998   Vec       cglobalo, clocal;
9999   Vec       fglobal, fglobalo, flocal;
10000   PetscBool regular;
10001 
10002   PetscFunctionBegin;
10003   PetscCall(DMGetFullDM(dmc, &dmco));
10004   PetscCall(DMGetFullDM(dmf, &dmfo));
10005   PetscCall(DMSetCoarseDM(dmfo, dmco));
10006   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
10007   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
10008   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
10009   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
10010   PetscCall(DMCreateLocalVector(dmc, &clocal));
10011   PetscCall(VecSet(cglobalo, 0.));
10012   PetscCall(VecSet(clocal, 0.));
10013   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
10014   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
10015   PetscCall(DMCreateLocalVector(dmf, &flocal));
10016   PetscCall(VecSet(fglobal, 0.));
10017   PetscCall(VecSet(fglobalo, 0.));
10018   PetscCall(VecSet(flocal, 0.));
10019   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
10020   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
10021   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
10022   PetscCall(MatMult(interpo, cglobalo, fglobalo));
10023   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
10024   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
10025   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
10026   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
10027   *shift = fglobal;
10028   PetscCall(VecDestroy(&flocal));
10029   PetscCall(VecDestroy(&fglobalo));
10030   PetscCall(VecDestroy(&clocal));
10031   PetscCall(VecDestroy(&cglobalo));
10032   PetscCall(VecDestroy(&rscale));
10033   PetscCall(MatDestroy(&interpo));
10034   PetscCall(DMDestroy(&dmfo));
10035   PetscCall(DMDestroy(&dmco));
10036   PetscFunctionReturn(PETSC_SUCCESS);
10037 }
10038 
10039 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
10040 {
10041   PetscObject shifto;
10042   Vec         shift;
10043 
10044   PetscFunctionBegin;
10045   if (!interp) {
10046     Vec rscale;
10047 
10048     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
10049     PetscCall(VecDestroy(&rscale));
10050   } else {
10051     PetscCall(PetscObjectReference((PetscObject)interp));
10052   }
10053   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
10054   if (!shifto) {
10055     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
10056     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
10057     shifto = (PetscObject)shift;
10058     PetscCall(VecDestroy(&shift));
10059   }
10060   shift = (Vec)shifto;
10061   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
10062   PetscCall(VecAXPY(fineSol, 1.0, shift));
10063   PetscCall(MatDestroy(&interp));
10064   PetscFunctionReturn(PETSC_SUCCESS);
10065 }
10066 
10067 /* Pointwise interpolation
10068      Just code FEM for now
10069      u^f = I u^c
10070      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
10071      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
10072      I_{ij} = psi^f_i phi^c_j
10073 */
10074 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
10075 {
10076   PetscSection gsc, gsf;
10077   PetscInt     m, n;
10078   void        *ctx;
10079   DM           cdm;
10080   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
10081 
10082   PetscFunctionBegin;
10083   PetscCall(DMGetGlobalSection(dmFine, &gsf));
10084   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10085   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10086   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10087 
10088   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
10089   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
10090   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10091   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
10092   PetscCall(DMGetApplicationContext(dmFine, &ctx));
10093 
10094   PetscCall(DMGetCoarseDM(dmFine, &cdm));
10095   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10096   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
10097   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
10098   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
10099   if (scaling) {
10100     /* Use naive scaling */
10101     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
10102   }
10103   PetscFunctionReturn(PETSC_SUCCESS);
10104 }
10105 
10106 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
10107 {
10108   VecScatter ctx;
10109 
10110   PetscFunctionBegin;
10111   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
10112   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
10113   PetscCall(VecScatterDestroy(&ctx));
10114   PetscFunctionReturn(PETSC_SUCCESS);
10115 }
10116 
10117 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[])
10118 {
10119   const PetscInt f  = (PetscInt)PetscRealPart(constants[numConstants]);
10120   const PetscInt Nc = uOff[f + 1] - uOff[f];
10121   for (PetscInt c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
10122 }
10123 
10124 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *lmass, Vec *mass)
10125 {
10126   DM           dmc;
10127   PetscDS      ds;
10128   Vec          ones, locmass;
10129   IS           cellIS;
10130   PetscFormKey key;
10131   PetscInt     depth;
10132 
10133   PetscFunctionBegin;
10134   PetscCall(DMClone(dm, &dmc));
10135   PetscCall(DMCopyDisc(dm, dmc));
10136   PetscCall(DMGetDS(dmc, &ds));
10137   for (PetscInt f = 0; f < dmc->Nf; ++f) PetscCall(PetscDSSetJacobian(ds, f, f, g0_identity_private, NULL, NULL, NULL));
10138   if (mass) PetscCall(DMCreateGlobalVector(dm, mass));
10139   if (lmass) PetscCall(DMCreateLocalVector(dm, &locmass));
10140   else PetscCall(DMGetLocalVector(dm, &locmass));
10141   PetscCall(DMGetLocalVector(dm, &ones));
10142   PetscCall(DMPlexGetDepth(dm, &depth));
10143   PetscCall(DMGetStratumIS(dm, "depth", depth, &cellIS));
10144   PetscCall(VecSet(locmass, 0.0));
10145   PetscCall(VecSet(ones, 1.0));
10146   key.label = NULL;
10147   key.value = 0;
10148   key.field = 0;
10149   key.part  = 0;
10150   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
10151   PetscCall(ISDestroy(&cellIS));
10152   if (mass) {
10153     PetscCall(DMLocalToGlobalBegin(dm, locmass, ADD_VALUES, *mass));
10154     PetscCall(DMLocalToGlobalEnd(dm, locmass, ADD_VALUES, *mass));
10155   }
10156   PetscCall(DMRestoreLocalVector(dm, &ones));
10157   if (lmass) *lmass = locmass;
10158   else PetscCall(DMRestoreLocalVector(dm, &locmass));
10159   PetscCall(DMDestroy(&dmc));
10160   PetscFunctionReturn(PETSC_SUCCESS);
10161 }
10162 
10163 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
10164 {
10165   PetscSection gsc, gsf;
10166   PetscInt     m, n;
10167   void        *ctx;
10168   DM           cdm;
10169   PetscBool    regular;
10170 
10171   PetscFunctionBegin;
10172   if (dmFine == dmCoarse) {
10173     DM            dmc;
10174     PetscDS       ds;
10175     PetscWeakForm wf;
10176     Vec           u;
10177     IS            cellIS;
10178     PetscFormKey  key;
10179     PetscInt      depth;
10180 
10181     PetscCall(DMClone(dmFine, &dmc));
10182     PetscCall(DMCopyDisc(dmFine, dmc));
10183     PetscCall(DMGetDS(dmc, &ds));
10184     PetscCall(PetscDSGetWeakForm(ds, &wf));
10185     PetscCall(PetscWeakFormClear(wf));
10186     for (PetscInt f = 0; f < dmc->Nf; ++f) PetscCall(PetscDSSetJacobian(ds, f, f, g0_identity_private, NULL, NULL, NULL));
10187     PetscCall(DMCreateMatrix(dmc, mass));
10188     PetscCall(DMGetLocalVector(dmc, &u));
10189     PetscCall(DMPlexGetDepth(dmc, &depth));
10190     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
10191     PetscCall(MatZeroEntries(*mass));
10192     key.label = NULL;
10193     key.value = 0;
10194     key.field = 0;
10195     key.part  = 0;
10196     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
10197     PetscCall(ISDestroy(&cellIS));
10198     PetscCall(DMRestoreLocalVector(dmc, &u));
10199     PetscCall(DMDestroy(&dmc));
10200   } else {
10201     PetscCall(DMGetGlobalSection(dmFine, &gsf));
10202     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10203     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10204     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10205 
10206     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
10207     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10208     PetscCall(MatSetType(*mass, dmCoarse->mattype));
10209     PetscCall(DMGetApplicationContext(dmFine, &ctx));
10210 
10211     PetscCall(DMGetCoarseDM(dmFine, &cdm));
10212     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10213     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
10214     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
10215   }
10216   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
10217   PetscFunctionReturn(PETSC_SUCCESS);
10218 }
10219 
10220 /*@
10221   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10222 
10223   Input Parameter:
10224 . dm - The `DMPLEX` object
10225 
10226   Output Parameter:
10227 . regular - The flag
10228 
10229   Level: intermediate
10230 
10231 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
10232 @*/
10233 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
10234 {
10235   PetscFunctionBegin;
10236   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10237   PetscAssertPointer(regular, 2);
10238   *regular = ((DM_Plex *)dm->data)->regularRefinement;
10239   PetscFunctionReturn(PETSC_SUCCESS);
10240 }
10241 
10242 /*@
10243   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10244 
10245   Input Parameters:
10246 + dm      - The `DMPLEX` object
10247 - regular - The flag
10248 
10249   Level: intermediate
10250 
10251 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
10252 @*/
10253 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
10254 {
10255   PetscFunctionBegin;
10256   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10257   ((DM_Plex *)dm->data)->regularRefinement = regular;
10258   PetscFunctionReturn(PETSC_SUCCESS);
10259 }
10260 
10261 /*@
10262   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
10263   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
10264 
10265   Not Collective
10266 
10267   Input Parameter:
10268 . dm - The `DMPLEX` object
10269 
10270   Output Parameters:
10271 + anchorSection - If not `NULL`, set to the section describing which points anchor the constrained points.
10272 - anchorIS      - If not `NULL`, set to the list of anchors indexed by `anchorSection`
10273 
10274   Level: intermediate
10275 
10276 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
10277 @*/
10278 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
10279 {
10280   DM_Plex *plex = (DM_Plex *)dm->data;
10281 
10282   PetscFunctionBegin;
10283   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10284   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
10285   if (anchorSection) *anchorSection = plex->anchorSection;
10286   if (anchorIS) *anchorIS = plex->anchorIS;
10287   PetscFunctionReturn(PETSC_SUCCESS);
10288 }
10289 
10290 /*@
10291   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.
10292 
10293   Collective
10294 
10295   Input Parameters:
10296 + dm            - The `DMPLEX` object
10297 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
10298                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10299 - anchorIS      - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10300 
10301   Level: intermediate
10302 
10303   Notes:
10304   Unlike boundary conditions, when a point's degrees of freedom in a section are constrained to
10305   an outside value, the anchor constraints set a point's degrees of freedom to be a linear
10306   combination of other points' degrees of freedom.
10307 
10308   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
10309   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
10310 
10311   The reference counts of `anchorSection` and `anchorIS` are incremented.
10312 
10313 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
10314 @*/
10315 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
10316 {
10317   DM_Plex    *plex = (DM_Plex *)dm->data;
10318   PetscMPIInt result;
10319 
10320   PetscFunctionBegin;
10321   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10322   if (anchorSection) {
10323     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
10324     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
10325     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
10326   }
10327   if (anchorIS) {
10328     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
10329     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
10330     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
10331   }
10332 
10333   PetscCall(PetscObjectReference((PetscObject)anchorSection));
10334   PetscCall(PetscSectionDestroy(&plex->anchorSection));
10335   plex->anchorSection = anchorSection;
10336 
10337   PetscCall(PetscObjectReference((PetscObject)anchorIS));
10338   PetscCall(ISDestroy(&plex->anchorIS));
10339   plex->anchorIS = anchorIS;
10340 
10341   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
10342     PetscInt        size, a, pStart, pEnd;
10343     const PetscInt *anchors;
10344 
10345     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10346     PetscCall(ISGetLocalSize(anchorIS, &size));
10347     PetscCall(ISGetIndices(anchorIS, &anchors));
10348     for (a = 0; a < size; a++) {
10349       PetscInt p;
10350 
10351       p = anchors[a];
10352       if (p >= pStart && p < pEnd) {
10353         PetscInt dof;
10354 
10355         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10356         if (dof) {
10357           PetscCall(ISRestoreIndices(anchorIS, &anchors));
10358           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
10359         }
10360       }
10361     }
10362     PetscCall(ISRestoreIndices(anchorIS, &anchors));
10363   }
10364   /* reset the generic constraints */
10365   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
10366   PetscFunctionReturn(PETSC_SUCCESS);
10367 }
10368 
10369 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
10370 {
10371   PetscSection anchorSection;
10372   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
10373 
10374   PetscFunctionBegin;
10375   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10376   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10377   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
10378   PetscCall(PetscSectionGetNumFields(section, &numFields));
10379   if (numFields) {
10380     PetscInt f;
10381     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
10382 
10383     for (f = 0; f < numFields; f++) {
10384       PetscInt numComp;
10385 
10386       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
10387       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
10388     }
10389   }
10390   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10391   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10392   pStart = PetscMax(pStart, sStart);
10393   pEnd   = PetscMin(pEnd, sEnd);
10394   pEnd   = PetscMax(pStart, pEnd);
10395   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
10396   for (p = pStart; p < pEnd; p++) {
10397     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10398     if (dof) {
10399       PetscCall(PetscSectionGetDof(section, p, &dof));
10400       PetscCall(PetscSectionSetDof(*cSec, p, dof));
10401       for (f = 0; f < numFields; f++) {
10402         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
10403         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
10404       }
10405     }
10406   }
10407   PetscCall(PetscSectionSetUp(*cSec));
10408   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
10409   PetscFunctionReturn(PETSC_SUCCESS);
10410 }
10411 
10412 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
10413 {
10414   PetscSection    aSec;
10415   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
10416   const PetscInt *anchors;
10417   PetscInt        numFields, f;
10418   IS              aIS;
10419   MatType         mtype;
10420   PetscBool       iscuda, iskokkos;
10421 
10422   PetscFunctionBegin;
10423   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10424   PetscCall(PetscSectionGetStorageSize(cSec, &m));
10425   PetscCall(PetscSectionGetStorageSize(section, &n));
10426   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
10427   PetscCall(MatSetSizes(*cMat, m, n, m, n));
10428   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
10429   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
10430   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
10431   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
10432   if (iscuda) mtype = MATSEQAIJCUSPARSE;
10433   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
10434   else mtype = MATSEQAIJ;
10435   PetscCall(MatSetType(*cMat, mtype));
10436   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
10437   PetscCall(ISGetIndices(aIS, &anchors));
10438   /* cSec will be a subset of aSec and section */
10439   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
10440   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10441   PetscCall(PetscMalloc1(m + 1, &i));
10442   i[0] = 0;
10443   PetscCall(PetscSectionGetNumFields(section, &numFields));
10444   for (p = pStart; p < pEnd; p++) {
10445     PetscInt rDof, rOff, r;
10446 
10447     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10448     if (!rDof) continue;
10449     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10450     if (numFields) {
10451       for (f = 0; f < numFields; f++) {
10452         annz = 0;
10453         for (r = 0; r < rDof; r++) {
10454           a = anchors[rOff + r];
10455           if (a < sStart || a >= sEnd) continue;
10456           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10457           annz += aDof;
10458         }
10459         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10460         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
10461         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10462       }
10463     } else {
10464       annz = 0;
10465       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10466       for (q = 0; q < dof; q++) {
10467         a = anchors[rOff + q];
10468         if (a < sStart || a >= sEnd) continue;
10469         PetscCall(PetscSectionGetDof(section, a, &aDof));
10470         annz += aDof;
10471       }
10472       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10473       PetscCall(PetscSectionGetOffset(cSec, p, &off));
10474       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10475     }
10476   }
10477   nnz = i[m];
10478   PetscCall(PetscMalloc1(nnz, &j));
10479   offset = 0;
10480   for (p = pStart; p < pEnd; p++) {
10481     if (numFields) {
10482       for (f = 0; f < numFields; f++) {
10483         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10484         for (q = 0; q < dof; q++) {
10485           PetscInt rDof, rOff, r;
10486           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10487           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10488           for (r = 0; r < rDof; r++) {
10489             PetscInt s;
10490 
10491             a = anchors[rOff + r];
10492             if (a < sStart || a >= sEnd) continue;
10493             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10494             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
10495             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10496           }
10497         }
10498       }
10499     } else {
10500       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10501       for (q = 0; q < dof; q++) {
10502         PetscInt rDof, rOff, r;
10503         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10504         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10505         for (r = 0; r < rDof; r++) {
10506           PetscInt s;
10507 
10508           a = anchors[rOff + r];
10509           if (a < sStart || a >= sEnd) continue;
10510           PetscCall(PetscSectionGetDof(section, a, &aDof));
10511           PetscCall(PetscSectionGetOffset(section, a, &aOff));
10512           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10513         }
10514       }
10515     }
10516   }
10517   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
10518   PetscCall(PetscFree(i));
10519   PetscCall(PetscFree(j));
10520   PetscCall(ISRestoreIndices(aIS, &anchors));
10521   PetscFunctionReturn(PETSC_SUCCESS);
10522 }
10523 
10524 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
10525 {
10526   DM_Plex     *plex = (DM_Plex *)dm->data;
10527   PetscSection anchorSection, section, cSec;
10528   Mat          cMat;
10529 
10530   PetscFunctionBegin;
10531   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10532   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10533   if (anchorSection) {
10534     PetscInt Nf;
10535 
10536     PetscCall(DMGetLocalSection(dm, &section));
10537     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
10538     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
10539     PetscCall(DMGetNumFields(dm, &Nf));
10540     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
10541     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
10542     PetscCall(PetscSectionDestroy(&cSec));
10543     PetscCall(MatDestroy(&cMat));
10544   }
10545   PetscFunctionReturn(PETSC_SUCCESS);
10546 }
10547 
10548 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10549 {
10550   IS           subis;
10551   PetscSection section, subsection;
10552 
10553   PetscFunctionBegin;
10554   PetscCall(DMGetLocalSection(dm, &section));
10555   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10556   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10557   /* Create subdomain */
10558   PetscCall(DMPlexFilter(dm, label, value, PETSC_FALSE, PETSC_FALSE, NULL, subdm));
10559   /* Create submodel */
10560   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10561   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10562   PetscCall(DMSetLocalSection(*subdm, subsection));
10563   PetscCall(PetscSectionDestroy(&subsection));
10564   PetscCall(DMCopyDisc(dm, *subdm));
10565   /* Create map from submodel to global model */
10566   if (is) {
10567     PetscSection    sectionGlobal, subsectionGlobal;
10568     IS              spIS;
10569     const PetscInt *spmap;
10570     PetscInt       *subIndices;
10571     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10572     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10573 
10574     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10575     PetscCall(ISGetIndices(spIS, &spmap));
10576     PetscCall(PetscSectionGetNumFields(section, &Nf));
10577     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10578     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10579     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10580     for (p = pStart; p < pEnd; ++p) {
10581       PetscInt gdof, pSubSize = 0;
10582 
10583       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10584       if (gdof > 0) {
10585         for (f = 0; f < Nf; ++f) {
10586           PetscInt fdof, fcdof;
10587 
10588           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10589           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10590           pSubSize += fdof - fcdof;
10591         }
10592         subSize += pSubSize;
10593         if (pSubSize) {
10594           if (bs < 0) {
10595             bs = pSubSize;
10596           } else if (bs != pSubSize) {
10597             /* Layout does not admit a pointwise block size */
10598             bs = 1;
10599           }
10600         }
10601       }
10602     }
10603     /* Must have same blocksize on all procs (some might have no points) */
10604     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
10605     bsLocal[1] = bs;
10606     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10607     if (bsMinMax[0] != bsMinMax[1]) {
10608       bs = 1;
10609     } else {
10610       bs = bsMinMax[0];
10611     }
10612     PetscCall(PetscMalloc1(subSize, &subIndices));
10613     for (p = pStart; p < pEnd; ++p) {
10614       PetscInt gdof, goff;
10615 
10616       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10617       if (gdof > 0) {
10618         const PetscInt point = spmap[p];
10619 
10620         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10621         for (f = 0; f < Nf; ++f) {
10622           PetscInt fdof, fcdof, fc, f2, poff = 0;
10623 
10624           /* Can get rid of this loop by storing field information in the global section */
10625           for (f2 = 0; f2 < f; ++f2) {
10626             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10627             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10628             poff += fdof - fcdof;
10629           }
10630           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10631           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10632           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10633         }
10634       }
10635     }
10636     PetscCall(ISRestoreIndices(spIS, &spmap));
10637     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10638     if (bs > 1) {
10639       /* We need to check that the block size does not come from non-contiguous fields */
10640       PetscInt i, j, set = 1;
10641       for (i = 0; i < subSize; i += bs) {
10642         for (j = 0; j < bs; ++j) {
10643           if (subIndices[i + j] != subIndices[i] + j) {
10644             set = 0;
10645             break;
10646           }
10647         }
10648       }
10649       if (set) PetscCall(ISSetBlockSize(*is, bs));
10650     }
10651     /* Attach nullspace */
10652     for (f = 0; f < Nf; ++f) {
10653       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10654       if ((*subdm)->nullspaceConstructors[f]) break;
10655     }
10656     if (f < Nf) {
10657       MatNullSpace nullSpace;
10658       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10659 
10660       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10661       PetscCall(MatNullSpaceDestroy(&nullSpace));
10662     }
10663   }
10664   PetscFunctionReturn(PETSC_SUCCESS);
10665 }
10666 
10667 /*@
10668   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10669 
10670   Input Parameters:
10671 + dm    - The `DM`
10672 - dummy - unused argument
10673 
10674   Options Database Key:
10675 . -dm_plex_monitor_throughput - Activate the monitor
10676 
10677   Level: developer
10678 
10679 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10680 @*/
10681 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10682 {
10683   PetscLogHandler default_handler;
10684 
10685   PetscFunctionBegin;
10686   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10687   PetscCall(PetscLogGetDefaultHandler(&default_handler));
10688   if (default_handler) {
10689     PetscLogEvent      event;
10690     PetscEventPerfInfo eventInfo;
10691     PetscReal          cellRate, flopRate;
10692     PetscInt           cStart, cEnd, Nf, N;
10693     const char        *name;
10694 
10695     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10696     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10697     PetscCall(DMGetNumFields(dm, &Nf));
10698     PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10699     PetscCall(PetscLogEventGetPerfInfo(PETSC_DEFAULT, event, &eventInfo));
10700     N        = (cEnd - cStart) * Nf * eventInfo.count;
10701     flopRate = eventInfo.flops / eventInfo.time;
10702     cellRate = N / eventInfo.time;
10703     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)));
10704   } else {
10705     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.");
10706   }
10707   PetscFunctionReturn(PETSC_SUCCESS);
10708 }
10709