xref: /petsc/src/dm/impls/plex/plex.c (revision 3201ab8d70a3eea669f338f8bd746342de1cf5db)
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   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2784   PetscCall(PetscFree(mesh));
2785   PetscFunctionReturn(PETSC_SUCCESS);
2786 }
2787 
2788 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2789 {
2790   PetscSection           sectionGlobal, sectionLocal;
2791   PetscInt               bs = -1, mbs;
2792   PetscInt               localSize, localStart = 0;
2793   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2794   MatType                mtype;
2795   ISLocalToGlobalMapping ltog;
2796 
2797   PetscFunctionBegin;
2798   PetscCall(MatInitializePackage());
2799   mtype = dm->mattype;
2800   PetscCall(DMGetLocalSection(dm, &sectionLocal));
2801   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2802   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2803   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2804   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2805   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2806   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2807   PetscCall(MatSetType(*J, mtype));
2808   PetscCall(MatSetFromOptions(*J));
2809   PetscCall(MatGetBlockSize(*J, &mbs));
2810   if (mbs > 1) bs = mbs;
2811   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2812   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2813   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2814   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2815   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2816   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2817   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2818   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2819   if (!isShell) {
2820     // There are three states with pblocks, since block starts can have no dofs:
2821     // UNKNOWN) New Block:   An open block has been signalled by pblocks[p] == 1
2822     // TRUE)    Block Start: The first entry in a block has been added
2823     // FALSE)   Block Add:   An additional block entry has been added, since pblocks[p] == 0
2824     PetscBT         blst;
2825     PetscBool3      bstate     = PETSC_BOOL3_UNKNOWN;
2826     PetscBool       fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2827     const PetscInt *perm       = NULL;
2828     PetscInt       *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2829     PetscInt        pStart, pEnd, dof, cdof, num_fields;
2830 
2831     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2832     PetscCall(PetscSectionGetBlockStarts(sectionLocal, &blst));
2833     if (sectionLocal->perm) PetscCall(ISGetIndices(sectionLocal->perm, &perm));
2834 
2835     PetscCall(PetscCalloc1(localSize, &pblocks));
2836     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2837     PetscCall(PetscSectionGetNumFields(sectionGlobal, &num_fields));
2838     // We need to process in the permuted order to get block sizes right
2839     for (PetscInt point = pStart; point < pEnd; ++point) {
2840       const PetscInt p = perm ? perm[point] : point;
2841 
2842       switch (dm->blocking_type) {
2843       case DM_BLOCKING_TOPOLOGICAL_POINT: { // One block per topological point
2844         PetscInt bdof, offset;
2845 
2846         PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2847         PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2848         PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2849         if (blst && PetscBTLookup(blst, p)) bstate = PETSC_BOOL3_UNKNOWN;
2850         if (dof > 0) {
2851           // State change
2852           if (bstate == PETSC_BOOL3_UNKNOWN) bstate = PETSC_BOOL3_TRUE;
2853           else if (bstate == PETSC_BOOL3_TRUE && blst && !PetscBTLookup(blst, p)) bstate = PETSC_BOOL3_FALSE;
2854 
2855           for (PetscInt i = 0; i < dof - cdof; ++i) pblocks[offset - localStart + i] = dof - cdof;
2856           // Signal block concatenation
2857           if (bstate == PETSC_BOOL3_FALSE && dof - cdof) pblocks[offset - localStart] = -(dof - cdof);
2858         }
2859         dof  = dof < 0 ? -(dof + 1) : dof;
2860         bdof = cdof && (dof - cdof) ? 1 : dof;
2861         if (dof) {
2862           if (bs < 0) {
2863             bs = bdof;
2864           } else if (bs != bdof) {
2865             bs = 1;
2866           }
2867         }
2868       } break;
2869       case DM_BLOCKING_FIELD_NODE: {
2870         for (PetscInt field = 0; field < num_fields; field++) {
2871           PetscInt num_comp, bdof, offset;
2872           PetscCall(PetscSectionGetFieldComponents(sectionGlobal, field, &num_comp));
2873           PetscCall(PetscSectionGetFieldDof(sectionGlobal, p, field, &dof));
2874           if (dof < 0) continue;
2875           PetscCall(PetscSectionGetFieldOffset(sectionGlobal, p, field, &offset));
2876           PetscCall(PetscSectionGetFieldConstraintDof(sectionGlobal, p, field, &cdof));
2877           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);
2878           PetscInt num_nodes = dof / num_comp;
2879           for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = (dof - cdof) / num_nodes;
2880           // Handle possibly constant block size (unlikely)
2881           bdof = cdof && (dof - cdof) ? 1 : dof;
2882           if (dof) {
2883             if (bs < 0) {
2884               bs = bdof;
2885             } else if (bs != bdof) {
2886               bs = 1;
2887             }
2888           }
2889         }
2890       } break;
2891       }
2892     }
2893     if (sectionLocal->perm) PetscCall(ISRestoreIndices(sectionLocal->perm, &perm));
2894     /* Must have same blocksize on all procs (some might have no points) */
2895     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2896     bsLocal[1] = bs;
2897     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
2898     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2899     else bs = bsMinMax[0];
2900     bs = PetscMax(1, bs);
2901     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
2902     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2903       PetscCall(MatSetBlockSize(*J, bs));
2904       PetscCall(MatSetUp(*J));
2905     } else {
2906       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
2907       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2908       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2909     }
2910     if (pblocks) { // Consolidate blocks
2911       PetscInt nblocks = 0;
2912       pblocks[0]       = PetscAbs(pblocks[0]);
2913       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
2914         if (pblocks[i] == 0) continue;
2915         // Negative block size indicates the blocks should be concatenated
2916         if (pblocks[i] < 0) {
2917           pblocks[i] = -pblocks[i];
2918           pblocks[nblocks - 1] += pblocks[i];
2919         } else {
2920           pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2921         }
2922         for (PetscInt j = 1; j < pblocks[i]; j++)
2923           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);
2924       }
2925       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2926     }
2927     PetscCall(PetscFree(pblocks));
2928   }
2929   PetscCall(MatSetDM(*J, dm));
2930   PetscFunctionReturn(PETSC_SUCCESS);
2931 }
2932 
2933 /*@
2934   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2935 
2936   Not Collective
2937 
2938   Input Parameter:
2939 . dm - The `DMPLEX`
2940 
2941   Output Parameter:
2942 . subsection - The subdomain section
2943 
2944   Level: developer
2945 
2946 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscSection`
2947 @*/
2948 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2949 {
2950   DM_Plex *mesh = (DM_Plex *)dm->data;
2951 
2952   PetscFunctionBegin;
2953   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2954   if (!mesh->subdomainSection) {
2955     PetscSection section;
2956     PetscSF      sf;
2957 
2958     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
2959     PetscCall(DMGetLocalSection(dm, &section));
2960     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
2961     PetscCall(PetscSFDestroy(&sf));
2962   }
2963   *subsection = mesh->subdomainSection;
2964   PetscFunctionReturn(PETSC_SUCCESS);
2965 }
2966 
2967 /*@
2968   DMPlexGetChart - Return the interval for all mesh points [`pStart`, `pEnd`)
2969 
2970   Not Collective
2971 
2972   Input Parameter:
2973 . dm - The `DMPLEX`
2974 
2975   Output Parameters:
2976 + pStart - The first mesh point
2977 - pEnd   - The upper bound for mesh points
2978 
2979   Level: beginner
2980 
2981 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`
2982 @*/
2983 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
2984 {
2985   DM_Plex *mesh = (DM_Plex *)dm->data;
2986 
2987   PetscFunctionBegin;
2988   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2989   if (mesh->tr) PetscCall(DMPlexTransformGetChart(mesh->tr, pStart, pEnd));
2990   else PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
2991   PetscFunctionReturn(PETSC_SUCCESS);
2992 }
2993 
2994 /*@
2995   DMPlexSetChart - Set the interval for all mesh points [`pStart`, `pEnd`)
2996 
2997   Not Collective
2998 
2999   Input Parameters:
3000 + dm     - The `DMPLEX`
3001 . pStart - The first mesh point
3002 - pEnd   - The upper bound for mesh points
3003 
3004   Level: beginner
3005 
3006 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetChart()`
3007 @*/
3008 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
3009 {
3010   DM_Plex *mesh = (DM_Plex *)dm->data;
3011 
3012   PetscFunctionBegin;
3013   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3014   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
3015   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
3016   PetscCall(PetscFree(mesh->cellTypes));
3017   PetscFunctionReturn(PETSC_SUCCESS);
3018 }
3019 
3020 /*@
3021   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
3022 
3023   Not Collective
3024 
3025   Input Parameters:
3026 + dm - The `DMPLEX`
3027 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3028 
3029   Output Parameter:
3030 . size - The cone size for point `p`
3031 
3032   Level: beginner
3033 
3034 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3035 @*/
3036 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
3037 {
3038   DM_Plex *mesh = (DM_Plex *)dm->data;
3039 
3040   PetscFunctionBegin;
3041   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3042   PetscAssertPointer(size, 3);
3043   if (mesh->tr) PetscCall(DMPlexTransformGetConeSize(mesh->tr, p, size));
3044   else PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
3045   PetscFunctionReturn(PETSC_SUCCESS);
3046 }
3047 
3048 /*@
3049   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
3050 
3051   Not Collective
3052 
3053   Input Parameters:
3054 + dm   - The `DMPLEX`
3055 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3056 - size - The cone size for point `p`
3057 
3058   Level: beginner
3059 
3060   Note:
3061   This should be called after `DMPlexSetChart()`.
3062 
3063 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
3064 @*/
3065 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
3066 {
3067   DM_Plex *mesh = (DM_Plex *)dm->data;
3068 
3069   PetscFunctionBegin;
3070   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3071   PetscCheck(!mesh->tr, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Cannot call DMPlexSetConeSize() on a mesh with a transform defined.");
3072   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
3073   PetscFunctionReturn(PETSC_SUCCESS);
3074 }
3075 
3076 /*@C
3077   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
3078 
3079   Not Collective
3080 
3081   Input Parameters:
3082 + dm - The `DMPLEX`
3083 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3084 
3085   Output Parameter:
3086 . cone - An array of points which are on the in-edges for point `p`, the length of `cone` is the result of `DMPlexGetConeSize()`
3087 
3088   Level: beginner
3089 
3090   Fortran Notes:
3091   `cone` must be declared with
3092 .vb
3093   PetscInt, pointer :: cone(:)
3094 .ve
3095 
3096   You must also call `DMPlexRestoreCone()` after you finish using the array.
3097   `DMPlexRestoreCone()` is not needed/available in C.
3098 
3099 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`, `DMPlexRestoreCone()`
3100 @*/
3101 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
3102 {
3103   DM_Plex *mesh = (DM_Plex *)dm->data;
3104   PetscInt off;
3105 
3106   PetscFunctionBegin;
3107   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3108   PetscAssertPointer(cone, 3);
3109   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3110   *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3111   PetscFunctionReturn(PETSC_SUCCESS);
3112 }
3113 
3114 /*@
3115   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
3116 
3117   Not Collective
3118 
3119   Input Parameters:
3120 + dm - The `DMPLEX`
3121 - p  - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3122 
3123   Output Parameters:
3124 + pConesSection - `PetscSection` describing the layout of `pCones`
3125 - pCones        - An `IS` containing the points which are on the in-edges for the point set `p`
3126 
3127   Level: intermediate
3128 
3129 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`, `PetscSection`, `IS`
3130 @*/
3131 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
3132 {
3133   PetscSection cs, newcs;
3134   PetscInt    *cones;
3135   PetscInt    *newarr = NULL;
3136   PetscInt     n;
3137 
3138   PetscFunctionBegin;
3139   PetscCall(DMPlexGetCones(dm, &cones));
3140   PetscCall(DMPlexGetConeSection(dm, &cs));
3141   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
3142   if (pConesSection) *pConesSection = newcs;
3143   if (pCones) {
3144     PetscCall(PetscSectionGetStorageSize(newcs, &n));
3145     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
3146   }
3147   PetscFunctionReturn(PETSC_SUCCESS);
3148 }
3149 
3150 /*@
3151   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
3152 
3153   Not Collective
3154 
3155   Input Parameters:
3156 + dm     - The `DMPLEX`
3157 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3158 
3159   Output Parameter:
3160 . expandedPoints - An `IS` containing the of vertices recursively expanded from input points
3161 
3162   Level: advanced
3163 
3164   Notes:
3165   Like `DMPlexGetConeRecursive()` but returns only the 0-depth `IS` (i.e. vertices only) and no sections.
3166 
3167   There is no corresponding Restore function, just call `ISDestroy()` on the returned `IS` to deallocate.
3168 
3169 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`,
3170           `DMPlexGetDepth()`, `IS`
3171 @*/
3172 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
3173 {
3174   IS      *expandedPointsAll;
3175   PetscInt depth;
3176 
3177   PetscFunctionBegin;
3178   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3179   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3180   PetscAssertPointer(expandedPoints, 3);
3181   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3182   *expandedPoints = expandedPointsAll[0];
3183   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
3184   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3185   PetscFunctionReturn(PETSC_SUCCESS);
3186 }
3187 
3188 /*@
3189   DMPlexGetConeRecursive - Expand each given point into its cone points and do that recursively until we end up just with vertices
3190   (DAG points of depth 0, i.e., without cones).
3191 
3192   Not Collective
3193 
3194   Input Parameters:
3195 + dm     - The `DMPLEX`
3196 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3197 
3198   Output Parameters:
3199 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3200 . expandedPoints - (optional) An array of index sets with recursively expanded cones
3201 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3202 
3203   Level: advanced
3204 
3205   Notes:
3206   Like `DMPlexGetConeTuple()` but recursive.
3207 
3208   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.
3209   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
3210 
3211   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\:
3212   (1) DAG points in `expandedPoints`[d+1] with `depth` d+1 to their cone points in `expandedPoints`[d];
3213   (2) DAG points in `expandedPoints`[d+1] with `depth` in [0,d] to the same points in `expandedPoints`[d].
3214 
3215 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3216           `DMPlexGetDepth()`, `PetscSection`, `IS`
3217 @*/
3218 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3219 {
3220   const PetscInt *arr0 = NULL, *cone = NULL;
3221   PetscInt       *arr = NULL, *newarr = NULL;
3222   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
3223   IS             *expandedPoints_;
3224   PetscSection   *sections_;
3225 
3226   PetscFunctionBegin;
3227   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3228   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3229   if (depth) PetscAssertPointer(depth, 3);
3230   if (expandedPoints) PetscAssertPointer(expandedPoints, 4);
3231   if (sections) PetscAssertPointer(sections, 5);
3232   PetscCall(ISGetLocalSize(points, &n));
3233   PetscCall(ISGetIndices(points, &arr0));
3234   PetscCall(DMPlexGetDepth(dm, &depth_));
3235   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
3236   PetscCall(PetscCalloc1(depth_, &sections_));
3237   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
3238   for (d = depth_ - 1; d >= 0; d--) {
3239     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
3240     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
3241     for (i = 0; i < n; i++) {
3242       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
3243       if (arr[i] >= start && arr[i] < end) {
3244         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
3245         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
3246       } else {
3247         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
3248       }
3249     }
3250     PetscCall(PetscSectionSetUp(sections_[d]));
3251     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
3252     PetscCall(PetscMalloc1(newn, &newarr));
3253     for (i = 0; i < n; i++) {
3254       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
3255       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
3256       if (cn > 1) {
3257         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
3258         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
3259       } else {
3260         newarr[co] = arr[i];
3261       }
3262     }
3263     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
3264     arr = newarr;
3265     n   = newn;
3266   }
3267   PetscCall(ISRestoreIndices(points, &arr0));
3268   *depth = depth_;
3269   if (expandedPoints) *expandedPoints = expandedPoints_;
3270   else {
3271     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
3272     PetscCall(PetscFree(expandedPoints_));
3273   }
3274   if (sections) *sections = sections_;
3275   else {
3276     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
3277     PetscCall(PetscFree(sections_));
3278   }
3279   PetscFunctionReturn(PETSC_SUCCESS);
3280 }
3281 
3282 /*@
3283   DMPlexRestoreConeRecursive - Deallocates arrays created by `DMPlexGetConeRecursive()`
3284 
3285   Not Collective
3286 
3287   Input Parameters:
3288 + dm     - The `DMPLEX`
3289 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3290 
3291   Output Parameters:
3292 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3293 . expandedPoints - (optional) An array of recursively expanded cones
3294 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3295 
3296   Level: advanced
3297 
3298   Note:
3299   See `DMPlexGetConeRecursive()`
3300 
3301 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3302           `DMPlexGetDepth()`, `IS`, `PetscSection`
3303 @*/
3304 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3305 {
3306   PetscInt d, depth_;
3307 
3308   PetscFunctionBegin;
3309   PetscCall(DMPlexGetDepth(dm, &depth_));
3310   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
3311   if (depth) *depth = 0;
3312   if (expandedPoints) {
3313     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
3314     PetscCall(PetscFree(*expandedPoints));
3315   }
3316   if (sections) {
3317     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
3318     PetscCall(PetscFree(*sections));
3319   }
3320   PetscFunctionReturn(PETSC_SUCCESS);
3321 }
3322 
3323 /*@
3324   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
3325 
3326   Not Collective
3327 
3328   Input Parameters:
3329 + dm   - The `DMPLEX`
3330 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3331 - cone - An array of points which are on the in-edges for point `p`, its length must have been previously provided with `DMPlexSetConeSize()`
3332 
3333   Level: beginner
3334 
3335   Note:
3336   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3337 
3338 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3339 @*/
3340 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3341 {
3342   DM_Plex *mesh = (DM_Plex *)dm->data;
3343   PetscInt dof, off, c;
3344 
3345   PetscFunctionBegin;
3346   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3347   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3348   if (dof) PetscAssertPointer(cone, 3);
3349   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3350   if (PetscDefined(USE_DEBUG)) {
3351     PetscInt pStart, pEnd;
3352     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3353     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);
3354     for (c = 0; c < dof; ++c) {
3355       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);
3356       mesh->cones[off + c] = cone[c];
3357     }
3358   } else {
3359     for (c = 0; c < dof; ++c) mesh->cones[off + c] = cone[c];
3360   }
3361   PetscFunctionReturn(PETSC_SUCCESS);
3362 }
3363 
3364 /*@C
3365   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3366 
3367   Not Collective
3368 
3369   Input Parameters:
3370 + dm - The `DMPLEX`
3371 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3372 
3373   Output Parameter:
3374 . coneOrientation - An array of orientations which are on the in-edges for point `p`. An orientation is an
3375                     integer giving the prescription for cone traversal. Its length is given by the result of `DMPlexSetConeSize()`
3376 
3377   Level: beginner
3378 
3379   Note:
3380   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3381   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3382   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3383   with the identity.
3384 
3385   Fortran Notes:
3386   You must call `DMPlexRestoreConeOrientation()` after you finish using the returned array.
3387   `DMPlexRestoreConeOrientation()` is not needed/available in C.
3388 
3389 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetConeSize()`, `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`,
3390           `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3391 @*/
3392 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3393 {
3394   DM_Plex *mesh = (DM_Plex *)dm->data;
3395   PetscInt off;
3396 
3397   PetscFunctionBegin;
3398   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3399   if (PetscDefined(USE_DEBUG)) {
3400     PetscInt dof;
3401     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3402     if (dof) PetscAssertPointer(coneOrientation, 3);
3403   }
3404   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3405 
3406   *coneOrientation = &mesh->coneOrientations[off];
3407   PetscFunctionReturn(PETSC_SUCCESS);
3408 }
3409 
3410 /*@
3411   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3412 
3413   Not Collective
3414 
3415   Input Parameters:
3416 + dm              - The `DMPLEX`
3417 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3418 - coneOrientation - An array of orientations. Its length is given by the result of `DMPlexSetConeSize()`
3419 
3420   Level: beginner
3421 
3422   Notes:
3423   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3424 
3425   The meaning of coneOrientation is detailed in `DMPlexGetConeOrientation()`.
3426 
3427 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3428 @*/
3429 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3430 {
3431   DM_Plex *mesh = (DM_Plex *)dm->data;
3432   PetscInt pStart, pEnd;
3433   PetscInt dof, off, c;
3434 
3435   PetscFunctionBegin;
3436   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3437   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3438   if (dof) PetscAssertPointer(coneOrientation, 3);
3439   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3440   if (PetscDefined(USE_DEBUG)) {
3441     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3442     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);
3443     for (c = 0; c < dof; ++c) {
3444       PetscInt cdof, o = coneOrientation[c];
3445 
3446       PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3447       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);
3448       mesh->coneOrientations[off + c] = o;
3449     }
3450   } else {
3451     for (c = 0; c < dof; ++c) mesh->coneOrientations[off + c] = coneOrientation[c];
3452   }
3453   PetscFunctionReturn(PETSC_SUCCESS);
3454 }
3455 
3456 /*@
3457   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3458 
3459   Not Collective
3460 
3461   Input Parameters:
3462 + dm        - The `DMPLEX`
3463 . p         - The point, which must lie in the chart set with `DMPlexSetChart()`
3464 . conePos   - The local index in the cone where the point should be put
3465 - conePoint - The mesh point to insert
3466 
3467   Level: beginner
3468 
3469 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3470 @*/
3471 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3472 {
3473   DM_Plex *mesh = (DM_Plex *)dm->data;
3474   PetscInt pStart, pEnd;
3475   PetscInt dof, off;
3476 
3477   PetscFunctionBegin;
3478   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3479   if (PetscDefined(USE_DEBUG)) {
3480     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3481     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);
3482     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);
3483     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3484     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);
3485   }
3486   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3487   mesh->cones[off + conePos] = conePoint;
3488   PetscFunctionReturn(PETSC_SUCCESS);
3489 }
3490 
3491 /*@
3492   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3493 
3494   Not Collective
3495 
3496   Input Parameters:
3497 + dm              - The `DMPLEX`
3498 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3499 . conePos         - The local index in the cone where the point should be put
3500 - coneOrientation - The point orientation to insert
3501 
3502   Level: beginner
3503 
3504   Note:
3505   The meaning of coneOrientation values is detailed in `DMPlexGetConeOrientation()`.
3506 
3507 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3508 @*/
3509 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3510 {
3511   DM_Plex *mesh = (DM_Plex *)dm->data;
3512   PetscInt pStart, pEnd;
3513   PetscInt dof, off;
3514 
3515   PetscFunctionBegin;
3516   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3517   if (PetscDefined(USE_DEBUG)) {
3518     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3519     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);
3520     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3521     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);
3522   }
3523   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3524   mesh->coneOrientations[off + conePos] = coneOrientation;
3525   PetscFunctionReturn(PETSC_SUCCESS);
3526 }
3527 
3528 /*@C
3529   DMPlexGetOrientedCone - Return the points and orientations on the in-edges for this point in the DAG
3530 
3531   Not collective
3532 
3533   Input Parameters:
3534 + dm - The DMPlex
3535 - p  - The point, which must lie in the chart set with DMPlexSetChart()
3536 
3537   Output Parameters:
3538 + cone - An array of points which are on the in-edges for point `p`
3539 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3540          integer giving the prescription for cone traversal.
3541 
3542   Level: beginner
3543 
3544   Notes:
3545   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3546   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3547   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3548   with the identity.
3549 
3550   You must also call `DMPlexRestoreOrientedCone()` after you finish using the returned array.
3551 
3552   Fortran Notes:
3553   `cone` and `ornt` must be declared with
3554 .vb
3555   PetscInt, pointer :: cone(:)
3556   PetscInt, pointer :: ornt(:)
3557 .ve
3558 
3559 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3560 @*/
3561 PetscErrorCode DMPlexGetOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3562 {
3563   DM_Plex *mesh = (DM_Plex *)dm->data;
3564 
3565   PetscFunctionBegin;
3566   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3567   if (mesh->tr) {
3568     PetscCall(DMPlexTransformGetCone(mesh->tr, p, cone, ornt));
3569   } else {
3570     PetscInt off;
3571     if (PetscDefined(USE_DEBUG)) {
3572       PetscInt dof;
3573       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3574       if (dof) {
3575         if (cone) PetscAssertPointer(cone, 3);
3576         if (ornt) PetscAssertPointer(ornt, 4);
3577       }
3578     }
3579     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3580     if (cone) *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3581     if (ornt) *ornt = PetscSafePointerPlusOffset(mesh->coneOrientations, off);
3582   }
3583   PetscFunctionReturn(PETSC_SUCCESS);
3584 }
3585 
3586 /*@C
3587   DMPlexRestoreOrientedCone - Restore the points and orientations on the in-edges for this point in the DAG obtained with `DMPlexGetOrientedCone()`
3588 
3589   Not Collective
3590 
3591   Input Parameters:
3592 + dm   - The DMPlex
3593 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3594 . cone - An array of points which are on the in-edges for point p
3595 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3596          integer giving the prescription for cone traversal.
3597 
3598   Level: beginner
3599 
3600 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3601 @*/
3602 PetscErrorCode DMPlexRestoreOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3603 {
3604   DM_Plex *mesh = (DM_Plex *)dm->data;
3605 
3606   PetscFunctionBegin;
3607   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3608   if (mesh->tr) PetscCall(DMPlexTransformRestoreCone(mesh->tr, p, cone, ornt));
3609   PetscFunctionReturn(PETSC_SUCCESS);
3610 }
3611 
3612 /*@
3613   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3614 
3615   Not Collective
3616 
3617   Input Parameters:
3618 + dm - The `DMPLEX`
3619 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3620 
3621   Output Parameter:
3622 . size - The support size for point `p`
3623 
3624   Level: beginner
3625 
3626 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3627 @*/
3628 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3629 {
3630   DM_Plex *mesh = (DM_Plex *)dm->data;
3631 
3632   PetscFunctionBegin;
3633   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3634   PetscAssertPointer(size, 3);
3635   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3636   PetscFunctionReturn(PETSC_SUCCESS);
3637 }
3638 
3639 /*@
3640   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3641 
3642   Not Collective
3643 
3644   Input Parameters:
3645 + dm   - The `DMPLEX`
3646 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3647 - size - The support size for point `p`
3648 
3649   Level: beginner
3650 
3651   Note:
3652   This should be called after `DMPlexSetChart()`.
3653 
3654 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3655 @*/
3656 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3657 {
3658   DM_Plex *mesh = (DM_Plex *)dm->data;
3659 
3660   PetscFunctionBegin;
3661   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3662   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3663   PetscFunctionReturn(PETSC_SUCCESS);
3664 }
3665 
3666 /*@C
3667   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3668 
3669   Not Collective
3670 
3671   Input Parameters:
3672 + dm - The `DMPLEX`
3673 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3674 
3675   Output Parameter:
3676 . support - An array of points which are on the out-edges for point `p`, its length is that obtained from `DMPlexGetSupportSize()`
3677 
3678   Level: beginner
3679 
3680   Fortran Notes:
3681   `support` must be declared with
3682 .vb
3683   PetscInt, pointer :: support(:)
3684 .ve
3685 
3686   You must also call `DMPlexRestoreSupport()` after you finish using the returned array.
3687   `DMPlexRestoreSupport()` is not needed/available in C.
3688 
3689 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3690 @*/
3691 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3692 {
3693   DM_Plex *mesh = (DM_Plex *)dm->data;
3694   PetscInt off;
3695 
3696   PetscFunctionBegin;
3697   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3698   PetscAssertPointer(support, 3);
3699   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3700   *support = PetscSafePointerPlusOffset(mesh->supports, off);
3701   PetscFunctionReturn(PETSC_SUCCESS);
3702 }
3703 
3704 /*@
3705   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3706 
3707   Not Collective
3708 
3709   Input Parameters:
3710 + dm      - The `DMPLEX`
3711 . p       - The point, which must lie in the chart set with `DMPlexSetChart()`
3712 - support - An array of points which are on the out-edges for point `p`, its length is that obtained from `DMPlexGetSupportSize()`
3713 
3714   Level: beginner
3715 
3716   Note:
3717   This should be called after all calls to `DMPlexSetSupportSize()` and `DMSetUp()`.
3718 
3719 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3720 @*/
3721 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3722 {
3723   DM_Plex *mesh = (DM_Plex *)dm->data;
3724   PetscInt pStart, pEnd;
3725   PetscInt dof, off, c;
3726 
3727   PetscFunctionBegin;
3728   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3729   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3730   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3731   if (dof) PetscAssertPointer(support, 3);
3732   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3733   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);
3734   for (c = 0; c < dof; ++c) {
3735     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);
3736     mesh->supports[off + c] = support[c];
3737   }
3738   PetscFunctionReturn(PETSC_SUCCESS);
3739 }
3740 
3741 /*@
3742   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3743 
3744   Not Collective
3745 
3746   Input Parameters:
3747 + dm           - The `DMPLEX`
3748 . p            - The point, which must lie in the chart set with `DMPlexSetChart()`
3749 . supportPos   - The local index in the cone where the point should be put
3750 - supportPoint - The mesh point to insert
3751 
3752   Level: beginner
3753 
3754 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3755 @*/
3756 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3757 {
3758   DM_Plex *mesh = (DM_Plex *)dm->data;
3759   PetscInt pStart, pEnd;
3760   PetscInt dof, off;
3761 
3762   PetscFunctionBegin;
3763   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3764   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3765   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3766   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3767   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);
3768   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);
3769   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);
3770   mesh->supports[off + supportPos] = supportPoint;
3771   PetscFunctionReturn(PETSC_SUCCESS);
3772 }
3773 
3774 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3775 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3776 {
3777   switch (ct) {
3778   case DM_POLYTOPE_SEGMENT:
3779     if (o == -1) return -2;
3780     break;
3781   case DM_POLYTOPE_TRIANGLE:
3782     if (o == -3) return -1;
3783     if (o == -2) return -3;
3784     if (o == -1) return -2;
3785     break;
3786   case DM_POLYTOPE_QUADRILATERAL:
3787     if (o == -4) return -2;
3788     if (o == -3) return -1;
3789     if (o == -2) return -4;
3790     if (o == -1) return -3;
3791     break;
3792   default:
3793     return o;
3794   }
3795   return o;
3796 }
3797 
3798 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3799 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3800 {
3801   switch (ct) {
3802   case DM_POLYTOPE_SEGMENT:
3803     if ((o == -2) || (o == 1)) return -1;
3804     if (o == -1) return 0;
3805     break;
3806   case DM_POLYTOPE_TRIANGLE:
3807     if (o == -3) return -2;
3808     if (o == -2) return -1;
3809     if (o == -1) return -3;
3810     break;
3811   case DM_POLYTOPE_QUADRILATERAL:
3812     if (o == -4) return -2;
3813     if (o == -3) return -1;
3814     if (o == -2) return -4;
3815     if (o == -1) return -3;
3816     break;
3817   default:
3818     return o;
3819   }
3820   return o;
3821 }
3822 
3823 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3824 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3825 {
3826   PetscInt pStart, pEnd, p;
3827 
3828   PetscFunctionBegin;
3829   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3830   for (p = pStart; p < pEnd; ++p) {
3831     const PetscInt *cone, *ornt;
3832     PetscInt        coneSize, c;
3833 
3834     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3835     PetscCall(DMPlexGetCone(dm, p, &cone));
3836     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3837     for (c = 0; c < coneSize; ++c) {
3838       DMPolytopeType ct;
3839       const PetscInt o = ornt[c];
3840 
3841       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3842       switch (ct) {
3843       case DM_POLYTOPE_SEGMENT:
3844         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3845         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3846         break;
3847       case DM_POLYTOPE_TRIANGLE:
3848         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3849         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3850         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3851         break;
3852       case DM_POLYTOPE_QUADRILATERAL:
3853         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3854         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3855         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3856         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3857         break;
3858       default:
3859         break;
3860       }
3861     }
3862   }
3863   PetscFunctionReturn(PETSC_SUCCESS);
3864 }
3865 
3866 static inline PetscErrorCode DMPlexGetTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3867 {
3868   DM_Plex *mesh = (DM_Plex *)dm->data;
3869 
3870   PetscFunctionBeginHot;
3871   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3872     if (useCone) {
3873       PetscCall(DMPlexGetConeSize(dm, p, size));
3874       PetscCall(DMPlexGetOrientedCone(dm, p, arr, ornt));
3875     } else {
3876       PetscCall(DMPlexGetSupportSize(dm, p, size));
3877       PetscCall(DMPlexGetSupport(dm, p, arr));
3878     }
3879   } else {
3880     if (useCone) {
3881       const PetscSection s   = mesh->coneSection;
3882       const PetscInt     ps  = p - s->pStart;
3883       const PetscInt     off = s->atlasOff[ps];
3884 
3885       *size = s->atlasDof[ps];
3886       *arr  = mesh->cones + off;
3887       *ornt = mesh->coneOrientations + off;
3888     } else {
3889       const PetscSection s   = mesh->supportSection;
3890       const PetscInt     ps  = p - s->pStart;
3891       const PetscInt     off = s->atlasOff[ps];
3892 
3893       *size = s->atlasDof[ps];
3894       *arr  = mesh->supports + off;
3895     }
3896   }
3897   PetscFunctionReturn(PETSC_SUCCESS);
3898 }
3899 
3900 static inline PetscErrorCode DMPlexRestoreTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3901 {
3902   DM_Plex *mesh = (DM_Plex *)dm->data;
3903 
3904   PetscFunctionBeginHot;
3905   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3906     if (useCone) PetscCall(DMPlexRestoreOrientedCone(dm, p, arr, ornt));
3907   }
3908   PetscFunctionReturn(PETSC_SUCCESS);
3909 }
3910 
3911 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3912 {
3913   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3914   PetscInt       *closure;
3915   const PetscInt *tmp = NULL, *tmpO = NULL;
3916   PetscInt        off = 0, tmpSize, t;
3917 
3918   PetscFunctionBeginHot;
3919   if (ornt) {
3920     PetscCall(DMPlexGetCellType(dm, p, &ct));
3921     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;
3922   }
3923   if (*points) {
3924     closure = *points;
3925   } else {
3926     PetscInt maxConeSize, maxSupportSize;
3927     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3928     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
3929   }
3930   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3931   if (ct == DM_POLYTOPE_UNKNOWN) {
3932     closure[off++] = p;
3933     closure[off++] = 0;
3934     for (t = 0; t < tmpSize; ++t) {
3935       closure[off++] = tmp[t];
3936       closure[off++] = tmpO ? tmpO[t] : 0;
3937     }
3938   } else {
3939     const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, ornt);
3940 
3941     /* We assume that cells with a valid type have faces with a valid type */
3942     closure[off++] = p;
3943     closure[off++] = ornt;
3944     for (t = 0; t < tmpSize; ++t) {
3945       DMPolytopeType ft;
3946 
3947       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3948       closure[off++] = tmp[arr[t]];
3949       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3950     }
3951   }
3952   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3953   if (numPoints) *numPoints = tmpSize + 1;
3954   if (points) *points = closure;
3955   PetscFunctionReturn(PETSC_SUCCESS);
3956 }
3957 
3958 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
3959 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3960 {
3961   const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, o);
3962   const PetscInt *cone, *ornt;
3963   PetscInt       *pts, *closure = NULL;
3964   DMPolytopeType  ft;
3965   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3966   PetscInt        dim, coneSize, c, d, clSize, cl;
3967 
3968   PetscFunctionBeginHot;
3969   PetscCall(DMGetDimension(dm, &dim));
3970   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3971   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3972   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
3973   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
3974   maxSize       = PetscMax(coneSeries, supportSeries);
3975   if (*points) {
3976     pts = *points;
3977   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
3978   c        = 0;
3979   pts[c++] = point;
3980   pts[c++] = o;
3981   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
3982   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
3983   for (cl = 0; cl < clSize * 2; cl += 2) {
3984     pts[c++] = closure[cl];
3985     pts[c++] = closure[cl + 1];
3986   }
3987   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
3988   for (cl = 0; cl < clSize * 2; cl += 2) {
3989     pts[c++] = closure[cl];
3990     pts[c++] = closure[cl + 1];
3991   }
3992   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3993   for (d = 2; d < coneSize; ++d) {
3994     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
3995     pts[c++] = cone[arr[d * 2 + 0]];
3996     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
3997   }
3998   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3999   if (dim >= 3) {
4000     for (d = 2; d < coneSize; ++d) {
4001       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
4002       const PetscInt *fcone, *fornt;
4003       PetscInt        fconeSize, fc, i;
4004 
4005       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
4006       const PetscInt *farr = DMPolytopeTypeGetArrangement(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
4007       PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
4008       for (fc = 0; fc < fconeSize; ++fc) {
4009         const PetscInt cp = fcone[farr[fc * 2 + 0]];
4010         const PetscInt co = farr[fc * 2 + 1];
4011 
4012         for (i = 0; i < c; i += 2)
4013           if (pts[i] == cp) break;
4014         if (i == c) {
4015           PetscCall(DMPlexGetCellType(dm, cp, &ft));
4016           pts[c++] = cp;
4017           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
4018         }
4019       }
4020       PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
4021     }
4022   }
4023   *numPoints = c / 2;
4024   *points    = pts;
4025   PetscFunctionReturn(PETSC_SUCCESS);
4026 }
4027 
4028 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4029 {
4030   DMPolytopeType ct;
4031   PetscInt      *closure, *fifo;
4032   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
4033   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
4034   PetscInt       depth, maxSize;
4035 
4036   PetscFunctionBeginHot;
4037   PetscCall(DMPlexGetDepth(dm, &depth));
4038   if (depth == 1) {
4039     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
4040     PetscFunctionReturn(PETSC_SUCCESS);
4041   }
4042   PetscCall(DMPlexGetCellType(dm, p, &ct));
4043   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;
4044   if (DMPolytopeTypeIsHybrid(ct) && ct != DM_POLYTOPE_POINT_PRISM_TENSOR) {
4045     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
4046     PetscFunctionReturn(PETSC_SUCCESS);
4047   }
4048   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
4049   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
4050   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
4051   maxSize       = PetscMax(coneSeries, supportSeries);
4052   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
4053   if (*points) {
4054     closure = *points;
4055   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
4056   closure[closureSize++] = p;
4057   closure[closureSize++] = ornt;
4058   fifo[fifoSize++]       = p;
4059   fifo[fifoSize++]       = ornt;
4060   fifo[fifoSize++]       = ct;
4061   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
4062   while (fifoSize - fifoStart) {
4063     const PetscInt       q    = fifo[fifoStart++];
4064     const PetscInt       o    = fifo[fifoStart++];
4065     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
4066     const PetscInt      *qarr = DMPolytopeTypeGetArrangement(qt, o);
4067     const PetscInt      *tmp, *tmpO = NULL;
4068     PetscInt             tmpSize, t;
4069 
4070     if (PetscDefined(USE_DEBUG)) {
4071       PetscInt nO = DMPolytopeTypeGetNumArrangements(qt) / 2;
4072       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);
4073     }
4074     PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4075     for (t = 0; t < tmpSize; ++t) {
4076       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
4077       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
4078       const PetscInt cp = tmp[ip];
4079       PetscCall(DMPlexGetCellType(dm, cp, &ct));
4080       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
4081       PetscInt       c;
4082 
4083       /* Check for duplicate */
4084       for (c = 0; c < closureSize; c += 2) {
4085         if (closure[c] == cp) break;
4086       }
4087       if (c == closureSize) {
4088         closure[closureSize++] = cp;
4089         closure[closureSize++] = co;
4090         fifo[fifoSize++]       = cp;
4091         fifo[fifoSize++]       = co;
4092         fifo[fifoSize++]       = ct;
4093       }
4094     }
4095     PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4096   }
4097   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
4098   if (numPoints) *numPoints = closureSize / 2;
4099   if (points) *points = closure;
4100   PetscFunctionReturn(PETSC_SUCCESS);
4101 }
4102 
4103 /*@C
4104   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
4105 
4106   Not Collective
4107 
4108   Input Parameters:
4109 + dm      - The `DMPLEX`
4110 . p       - The mesh point
4111 - useCone - `PETSC_TRUE` for the closure, otherwise return the star
4112 
4113   Input/Output Parameter:
4114 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
4115            if *points is `NULL` on input, internal storage will be returned, use `DMPlexRestoreTransitiveClosure()`,
4116            otherwise the provided array is used to hold the values
4117 
4118   Output Parameter:
4119 . numPoints - The number of points in the closure, so `points` is of size 2*`numPoints`
4120 
4121   Level: beginner
4122 
4123   Note:
4124   If using internal storage (points is `NULL` on input), each call overwrites the last output.
4125 
4126   Fortran Notes:
4127   `points` must be declared with
4128 .vb
4129   PetscInt, pointer :: points(:)
4130 .ve
4131   and is always allocated by the function.
4132 
4133   The `numPoints` argument is not present in the Fortran binding.
4134 
4135 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4136 @*/
4137 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4138 {
4139   PetscFunctionBeginHot;
4140   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4141   if (numPoints) PetscAssertPointer(numPoints, 4);
4142   if (points) PetscAssertPointer(points, 5);
4143   if (PetscDefined(USE_DEBUG)) {
4144     PetscInt pStart, pEnd;
4145     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4146     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);
4147   }
4148   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
4149   PetscFunctionReturn(PETSC_SUCCESS);
4150 }
4151 
4152 /*@C
4153   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
4154 
4155   Not Collective
4156 
4157   Input Parameters:
4158 + dm        - The `DMPLEX`
4159 . p         - The mesh point
4160 . useCone   - `PETSC_TRUE` for the closure, otherwise return the star
4161 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
4162 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
4163 
4164   Level: beginner
4165 
4166   Note:
4167   If not using internal storage (points is not `NULL` on input), this call is unnecessary
4168 
4169 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4170 @*/
4171 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4172 {
4173   PetscFunctionBeginHot;
4174   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4175   if (numPoints) *numPoints = 0;
4176   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
4177   PetscFunctionReturn(PETSC_SUCCESS);
4178 }
4179 
4180 /*@
4181   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
4182 
4183   Not Collective
4184 
4185   Input Parameter:
4186 . dm - The `DMPLEX`
4187 
4188   Output Parameters:
4189 + maxConeSize    - The maximum number of in-edges
4190 - maxSupportSize - The maximum number of out-edges
4191 
4192   Level: beginner
4193 
4194 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
4195 @*/
4196 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
4197 {
4198   DM_Plex *mesh = (DM_Plex *)dm->data;
4199 
4200   PetscFunctionBegin;
4201   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4202   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
4203   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
4204   PetscFunctionReturn(PETSC_SUCCESS);
4205 }
4206 
4207 PetscErrorCode DMSetUp_Plex(DM dm)
4208 {
4209   DM_Plex *mesh = (DM_Plex *)dm->data;
4210   PetscInt size, maxSupportSize;
4211 
4212   PetscFunctionBegin;
4213   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4214   PetscCall(PetscSectionSetUp(mesh->coneSection));
4215   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
4216   PetscCall(PetscMalloc1(size, &mesh->cones));
4217   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
4218   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4219   if (maxSupportSize) {
4220     PetscCall(PetscSectionSetUp(mesh->supportSection));
4221     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
4222     PetscCall(PetscMalloc1(size, &mesh->supports));
4223   }
4224   PetscFunctionReturn(PETSC_SUCCESS);
4225 }
4226 
4227 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
4228 {
4229   PetscFunctionBegin;
4230   if (subdm) PetscCall(DMClone(dm, subdm));
4231   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, NULL, NULL, is, subdm));
4232   if (subdm) (*subdm)->useNatural = dm->useNatural;
4233   if (dm->useNatural && dm->sfMigration) {
4234     PetscSF sfNatural;
4235 
4236     (*subdm)->sfMigration = dm->sfMigration;
4237     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
4238     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
4239     (*subdm)->sfNatural = sfNatural;
4240   }
4241   PetscFunctionReturn(PETSC_SUCCESS);
4242 }
4243 
4244 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
4245 {
4246   PetscInt i = 0;
4247 
4248   PetscFunctionBegin;
4249   PetscCall(DMClone(dms[0], superdm));
4250   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
4251   (*superdm)->useNatural = PETSC_FALSE;
4252   for (i = 0; i < len; i++) {
4253     if (dms[i]->useNatural && dms[i]->sfMigration) {
4254       PetscSF sfNatural;
4255 
4256       (*superdm)->sfMigration = dms[i]->sfMigration;
4257       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
4258       (*superdm)->useNatural = PETSC_TRUE;
4259       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
4260       (*superdm)->sfNatural = sfNatural;
4261       break;
4262     }
4263   }
4264   PetscFunctionReturn(PETSC_SUCCESS);
4265 }
4266 
4267 /*@
4268   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
4269 
4270   Not Collective
4271 
4272   Input Parameter:
4273 . dm - The `DMPLEX`
4274 
4275   Level: beginner
4276 
4277   Note:
4278   This should be called after all calls to `DMPlexSetCone()`
4279 
4280 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
4281 @*/
4282 PetscErrorCode DMPlexSymmetrize(DM dm)
4283 {
4284   DM_Plex  *mesh = (DM_Plex *)dm->data;
4285   PetscInt *offsets;
4286   PetscInt  supportSize;
4287   PetscInt  pStart, pEnd, p;
4288 
4289   PetscFunctionBegin;
4290   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4291   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
4292   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
4293   /* Calculate support sizes */
4294   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4295   for (p = pStart; p < pEnd; ++p) {
4296     PetscInt dof, off, c;
4297 
4298     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4299     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4300     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
4301   }
4302   PetscCall(PetscSectionSetUp(mesh->supportSection));
4303   /* Calculate supports */
4304   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
4305   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
4306   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
4307   for (p = pStart; p < pEnd; ++p) {
4308     PetscInt dof, off, c;
4309 
4310     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4311     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4312     for (c = off; c < off + dof; ++c) {
4313       const PetscInt q = mesh->cones[c];
4314       PetscInt       offS;
4315 
4316       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
4317 
4318       mesh->supports[offS + offsets[q]] = p;
4319       ++offsets[q];
4320     }
4321   }
4322   PetscCall(PetscFree(offsets));
4323   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
4324   PetscFunctionReturn(PETSC_SUCCESS);
4325 }
4326 
4327 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
4328 {
4329   IS stratumIS;
4330 
4331   PetscFunctionBegin;
4332   if (pStart >= pEnd) PetscFunctionReturn(PETSC_SUCCESS);
4333   if (PetscDefined(USE_DEBUG)) {
4334     PetscInt  qStart, qEnd, numLevels, level;
4335     PetscBool overlap = PETSC_FALSE;
4336     PetscCall(DMLabelGetNumValues(label, &numLevels));
4337     for (level = 0; level < numLevels; level++) {
4338       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4339       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
4340         overlap = PETSC_TRUE;
4341         break;
4342       }
4343     }
4344     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);
4345   }
4346   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
4347   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
4348   PetscCall(ISDestroy(&stratumIS));
4349   PetscFunctionReturn(PETSC_SUCCESS);
4350 }
4351 
4352 static PetscErrorCode DMPlexStratify_CellType_Private(DM dm, DMLabel label)
4353 {
4354   PetscInt *pMin, *pMax;
4355   PetscInt  pStart, pEnd;
4356   PetscInt  dmin = PETSC_MAX_INT, dmax = PETSC_MIN_INT;
4357 
4358   PetscFunctionBegin;
4359   {
4360     DMLabel label2;
4361 
4362     PetscCall(DMPlexGetCellTypeLabel(dm, &label2));
4363     PetscCall(PetscObjectViewFromOptions((PetscObject)label2, NULL, "-ct_view"));
4364   }
4365   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4366   for (PetscInt p = pStart; p < pEnd; ++p) {
4367     DMPolytopeType ct;
4368 
4369     PetscCall(DMPlexGetCellType(dm, p, &ct));
4370     dmin = PetscMin(DMPolytopeTypeGetDim(ct), dmin);
4371     dmax = PetscMax(DMPolytopeTypeGetDim(ct), dmax);
4372   }
4373   PetscCall(PetscMalloc2(dmax + 1, &pMin, dmax + 1, &pMax));
4374   for (PetscInt d = dmin; d <= dmax; ++d) {
4375     pMin[d] = PETSC_MAX_INT;
4376     pMax[d] = PETSC_MIN_INT;
4377   }
4378   for (PetscInt p = pStart; p < pEnd; ++p) {
4379     DMPolytopeType ct;
4380     PetscInt       d;
4381 
4382     PetscCall(DMPlexGetCellType(dm, p, &ct));
4383     d       = DMPolytopeTypeGetDim(ct);
4384     pMin[d] = PetscMin(p, pMin[d]);
4385     pMax[d] = PetscMax(p, pMax[d]);
4386   }
4387   for (PetscInt d = dmin; d <= dmax; ++d) {
4388     if (pMin[d] > pMax[d]) continue;
4389     PetscCall(DMPlexCreateDepthStratum(dm, label, d, pMin[d], pMax[d] + 1));
4390   }
4391   PetscCall(PetscFree2(pMin, pMax));
4392   PetscFunctionReturn(PETSC_SUCCESS);
4393 }
4394 
4395 static PetscErrorCode DMPlexStratify_Topological_Private(DM dm, DMLabel label)
4396 {
4397   PetscInt pStart, pEnd;
4398   PetscInt numRoots = 0, numLeaves = 0;
4399 
4400   PetscFunctionBegin;
4401   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4402   {
4403     /* Initialize roots and count leaves */
4404     PetscInt sMin = PETSC_MAX_INT;
4405     PetscInt sMax = PETSC_MIN_INT;
4406     PetscInt coneSize, supportSize;
4407 
4408     for (PetscInt p = pStart; p < pEnd; ++p) {
4409       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4410       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4411       if (!coneSize && supportSize) {
4412         sMin = PetscMin(p, sMin);
4413         sMax = PetscMax(p, sMax);
4414         ++numRoots;
4415       } else if (!supportSize && coneSize) {
4416         ++numLeaves;
4417       } else if (!supportSize && !coneSize) {
4418         /* Isolated points */
4419         sMin = PetscMin(p, sMin);
4420         sMax = PetscMax(p, sMax);
4421       }
4422     }
4423     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4424   }
4425 
4426   if (numRoots + numLeaves == (pEnd - pStart)) {
4427     PetscInt sMin = PETSC_MAX_INT;
4428     PetscInt sMax = PETSC_MIN_INT;
4429     PetscInt coneSize, supportSize;
4430 
4431     for (PetscInt p = pStart; p < pEnd; ++p) {
4432       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4433       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4434       if (!supportSize && coneSize) {
4435         sMin = PetscMin(p, sMin);
4436         sMax = PetscMax(p, sMax);
4437       }
4438     }
4439     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4440   } else {
4441     PetscInt level = 0;
4442     PetscInt qStart, qEnd;
4443 
4444     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4445     while (qEnd > qStart) {
4446       PetscInt sMin = PETSC_MAX_INT;
4447       PetscInt sMax = PETSC_MIN_INT;
4448 
4449       for (PetscInt q = qStart; q < qEnd; ++q) {
4450         const PetscInt *support;
4451         PetscInt        supportSize;
4452 
4453         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4454         PetscCall(DMPlexGetSupport(dm, q, &support));
4455         for (PetscInt s = 0; s < supportSize; ++s) {
4456           sMin = PetscMin(support[s], sMin);
4457           sMax = PetscMax(support[s], sMax);
4458         }
4459       }
4460       PetscCall(DMLabelGetNumValues(label, &level));
4461       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4462       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4463     }
4464   }
4465   PetscFunctionReturn(PETSC_SUCCESS);
4466 }
4467 
4468 /*@
4469   DMPlexStratify - Computes the strata for all points in the `DMPLEX`
4470 
4471   Collective
4472 
4473   Input Parameter:
4474 . dm - The `DMPLEX`
4475 
4476   Level: beginner
4477 
4478   Notes:
4479   The strata group all points of the same grade, and this function calculates the strata. This
4480   grade can be seen as the height (or depth) of the point in the DAG.
4481 
4482   The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
4483   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram).
4484   Concretely, `DMPlexStratify()` creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
4485   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
4486   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through `DMPlexGetDepthLabel()` or `DMPlexGetDepthStratum()`, or
4487   manually via `DMGetLabel()`.  The height is defined implicitly by height = maxDimension - depth, and can be accessed
4488   via `DMPlexGetHeightStratum()`.  For example, cells have height 0 and faces have height 1.
4489 
4490   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
4491   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
4492   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
4493   to interpolate only that one (e0), so that
4494 .vb
4495   cone(c0) = {e0, v2}
4496   cone(e0) = {v0, v1}
4497 .ve
4498   If `DMPlexStratify()` is run on this mesh, it will give depths
4499 .vb
4500    depth 0 = {v0, v1, v2}
4501    depth 1 = {e0, c0}
4502 .ve
4503   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
4504 
4505   `DMPlexStratify()` should be called after all calls to `DMPlexSymmetrize()`
4506 
4507 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
4508 @*/
4509 PetscErrorCode DMPlexStratify(DM dm)
4510 {
4511   DM_Plex  *mesh = (DM_Plex *)dm->data;
4512   DMLabel   label;
4513   PetscBool flg = PETSC_FALSE;
4514 
4515   PetscFunctionBegin;
4516   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4517   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
4518 
4519   // Create depth label
4520   PetscCall(DMRemoveLabel(dm, "depth", NULL));
4521   PetscCall(DMCreateLabel(dm, "depth"));
4522   PetscCall(DMPlexGetDepthLabel(dm, &label));
4523 
4524   PetscCall(PetscOptionsGetBool(NULL, dm->hdr.prefix, "-dm_plex_stratify_celltype", &flg, NULL));
4525   if (flg) PetscCall(DMPlexStratify_CellType_Private(dm, label));
4526   else PetscCall(DMPlexStratify_Topological_Private(dm, label));
4527 
4528   { /* just in case there is an empty process */
4529     PetscInt numValues, maxValues = 0, v;
4530 
4531     PetscCall(DMLabelGetNumValues(label, &numValues));
4532     PetscCall(MPIU_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4533     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4534   }
4535   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4536   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4537   PetscFunctionReturn(PETSC_SUCCESS);
4538 }
4539 
4540 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4541 {
4542   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4543   PetscInt       dim, depth, pheight, coneSize;
4544 
4545   PetscFunctionBeginHot;
4546   PetscCall(DMGetDimension(dm, &dim));
4547   PetscCall(DMPlexGetDepth(dm, &depth));
4548   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4549   pheight = depth - pdepth;
4550   if (depth <= 1) {
4551     switch (pdepth) {
4552     case 0:
4553       ct = DM_POLYTOPE_POINT;
4554       break;
4555     case 1:
4556       switch (coneSize) {
4557       case 2:
4558         ct = DM_POLYTOPE_SEGMENT;
4559         break;
4560       case 3:
4561         ct = DM_POLYTOPE_TRIANGLE;
4562         break;
4563       case 4:
4564         switch (dim) {
4565         case 2:
4566           ct = DM_POLYTOPE_QUADRILATERAL;
4567           break;
4568         case 3:
4569           ct = DM_POLYTOPE_TETRAHEDRON;
4570           break;
4571         default:
4572           break;
4573         }
4574         break;
4575       case 5:
4576         ct = DM_POLYTOPE_PYRAMID;
4577         break;
4578       case 6:
4579         ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4580         break;
4581       case 8:
4582         ct = DM_POLYTOPE_HEXAHEDRON;
4583         break;
4584       default:
4585         break;
4586       }
4587     }
4588   } else {
4589     if (pdepth == 0) {
4590       ct = DM_POLYTOPE_POINT;
4591     } else if (pheight == 0) {
4592       switch (dim) {
4593       case 1:
4594         switch (coneSize) {
4595         case 2:
4596           ct = DM_POLYTOPE_SEGMENT;
4597           break;
4598         default:
4599           break;
4600         }
4601         break;
4602       case 2:
4603         switch (coneSize) {
4604         case 3:
4605           ct = DM_POLYTOPE_TRIANGLE;
4606           break;
4607         case 4:
4608           ct = DM_POLYTOPE_QUADRILATERAL;
4609           break;
4610         default:
4611           break;
4612         }
4613         break;
4614       case 3:
4615         switch (coneSize) {
4616         case 4:
4617           ct = DM_POLYTOPE_TETRAHEDRON;
4618           break;
4619         case 5: {
4620           const PetscInt *cone;
4621           PetscInt        faceConeSize;
4622 
4623           PetscCall(DMPlexGetCone(dm, p, &cone));
4624           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4625           switch (faceConeSize) {
4626           case 3:
4627             ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4628             break;
4629           case 4:
4630             ct = DM_POLYTOPE_PYRAMID;
4631             break;
4632           }
4633         } break;
4634         case 6:
4635           ct = DM_POLYTOPE_HEXAHEDRON;
4636           break;
4637         default:
4638           break;
4639         }
4640         break;
4641       default:
4642         break;
4643       }
4644     } else if (pheight > 0) {
4645       switch (coneSize) {
4646       case 2:
4647         ct = DM_POLYTOPE_SEGMENT;
4648         break;
4649       case 3:
4650         ct = DM_POLYTOPE_TRIANGLE;
4651         break;
4652       case 4:
4653         ct = DM_POLYTOPE_QUADRILATERAL;
4654         break;
4655       default:
4656         break;
4657       }
4658     }
4659   }
4660   *pt = ct;
4661   PetscFunctionReturn(PETSC_SUCCESS);
4662 }
4663 
4664 /*@
4665   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4666 
4667   Collective
4668 
4669   Input Parameter:
4670 . dm - The `DMPLEX`
4671 
4672   Level: developer
4673 
4674   Note:
4675   This function is normally called automatically when a cell type is requested. It creates an
4676   internal `DMLabel` named "celltype" which can be directly accessed using `DMGetLabel()`. A user may disable
4677   automatic creation by creating the label manually, using `DMCreateLabel`(dm, "celltype").
4678 
4679   `DMPlexComputeCellTypes()` should be called after all calls to `DMPlexSymmetrize()` and `DMPlexStratify()`
4680 
4681 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4682 @*/
4683 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4684 {
4685   DM_Plex *mesh;
4686   DMLabel  ctLabel;
4687   PetscInt pStart, pEnd, p;
4688 
4689   PetscFunctionBegin;
4690   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4691   mesh = (DM_Plex *)dm->data;
4692   PetscCall(DMCreateLabel(dm, "celltype"));
4693   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4694   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4695   PetscCall(PetscFree(mesh->cellTypes));
4696   PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
4697   for (p = pStart; p < pEnd; ++p) {
4698     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4699     PetscInt       pdepth;
4700 
4701     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4702     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4703     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]);
4704     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4705     mesh->cellTypes[p - pStart].value_as_uint8 = ct;
4706   }
4707   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4708   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4709   PetscFunctionReturn(PETSC_SUCCESS);
4710 }
4711 
4712 /*@C
4713   DMPlexGetJoin - Get an array for the join of the set of points
4714 
4715   Not Collective
4716 
4717   Input Parameters:
4718 + dm        - The `DMPLEX` object
4719 . numPoints - The number of input points for the join
4720 - points    - The input points
4721 
4722   Output Parameters:
4723 + numCoveredPoints - The number of points in the join
4724 - coveredPoints    - The points in the join
4725 
4726   Level: intermediate
4727 
4728   Note:
4729   Currently, this is restricted to a single level join
4730 
4731   Fortran Notes:
4732   `converedPoints` must be declared with
4733 .vb
4734   PetscInt, pointer :: coveredPints(:)
4735 .ve
4736 
4737   The `numCoveredPoints` argument is not present in the Fortran binding.
4738 
4739 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4740 @*/
4741 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4742 {
4743   DM_Plex  *mesh = (DM_Plex *)dm->data;
4744   PetscInt *join[2];
4745   PetscInt  joinSize, i = 0;
4746   PetscInt  dof, off, p, c, m;
4747   PetscInt  maxSupportSize;
4748 
4749   PetscFunctionBegin;
4750   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4751   PetscAssertPointer(points, 3);
4752   PetscAssertPointer(numCoveredPoints, 4);
4753   PetscAssertPointer(coveredPoints, 5);
4754   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4755   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4756   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4757   /* Copy in support of first point */
4758   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4759   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4760   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4761   /* Check each successive support */
4762   for (p = 1; p < numPoints; ++p) {
4763     PetscInt newJoinSize = 0;
4764 
4765     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4766     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4767     for (c = 0; c < dof; ++c) {
4768       const PetscInt point = mesh->supports[off + c];
4769 
4770       for (m = 0; m < joinSize; ++m) {
4771         if (point == join[i][m]) {
4772           join[1 - i][newJoinSize++] = point;
4773           break;
4774         }
4775       }
4776     }
4777     joinSize = newJoinSize;
4778     i        = 1 - i;
4779   }
4780   *numCoveredPoints = joinSize;
4781   *coveredPoints    = join[i];
4782   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4783   PetscFunctionReturn(PETSC_SUCCESS);
4784 }
4785 
4786 /*@C
4787   DMPlexRestoreJoin - Restore an array for the join of the set of points obtained with `DMPlexGetJoin()`
4788 
4789   Not Collective
4790 
4791   Input Parameters:
4792 + dm        - The `DMPLEX` object
4793 . numPoints - The number of input points for the join
4794 - points    - The input points
4795 
4796   Output Parameters:
4797 + numCoveredPoints - The number of points in the join
4798 - coveredPoints    - The points in the join
4799 
4800   Level: intermediate
4801 
4802   Fortran Notes:
4803   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4804 
4805 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4806 @*/
4807 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4808 {
4809   PetscFunctionBegin;
4810   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4811   if (points) PetscAssertPointer(points, 3);
4812   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4813   PetscAssertPointer(coveredPoints, 5);
4814   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4815   if (numCoveredPoints) *numCoveredPoints = 0;
4816   PetscFunctionReturn(PETSC_SUCCESS);
4817 }
4818 
4819 /*@C
4820   DMPlexGetFullJoin - Get an array for the join of the set of points
4821 
4822   Not Collective
4823 
4824   Input Parameters:
4825 + dm        - The `DMPLEX` object
4826 . numPoints - The number of input points for the join
4827 - points    - The input points, its length is `numPoints`
4828 
4829   Output Parameters:
4830 + numCoveredPoints - The number of points in the join
4831 - coveredPoints    - The points in the join, its length is `numCoveredPoints`
4832 
4833   Level: intermediate
4834 
4835   Fortran Notes:
4836   `points` and `converedPoints` must be declared with
4837 .vb
4838   PetscInt, pointer :: points(:)
4839   PetscInt, pointer :: coveredPints(:)
4840 .ve
4841 
4842   The `numCoveredPoints` argument is not present in the Fortran binding.
4843 
4844 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4845 @*/
4846 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4847 {
4848   PetscInt *offsets, **closures;
4849   PetscInt *join[2];
4850   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4851   PetscInt  p, d, c, m, ms;
4852 
4853   PetscFunctionBegin;
4854   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4855   PetscAssertPointer(points, 3);
4856   PetscAssertPointer(numCoveredPoints, 4);
4857   PetscAssertPointer(coveredPoints, 5);
4858 
4859   PetscCall(DMPlexGetDepth(dm, &depth));
4860   PetscCall(PetscCalloc1(numPoints, &closures));
4861   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4862   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4863   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4864   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4865   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4866 
4867   for (p = 0; p < numPoints; ++p) {
4868     PetscInt closureSize;
4869 
4870     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4871 
4872     offsets[p * (depth + 2) + 0] = 0;
4873     for (d = 0; d < depth + 1; ++d) {
4874       PetscInt pStart, pEnd, i;
4875 
4876       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4877       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4878         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4879           offsets[p * (depth + 2) + d + 1] = i;
4880           break;
4881         }
4882       }
4883       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4884     }
4885     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);
4886   }
4887   for (d = 0; d < depth + 1; ++d) {
4888     PetscInt dof;
4889 
4890     /* Copy in support of first point */
4891     dof = offsets[d + 1] - offsets[d];
4892     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4893     /* Check each successive cone */
4894     for (p = 1; p < numPoints && joinSize; ++p) {
4895       PetscInt newJoinSize = 0;
4896 
4897       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
4898       for (c = 0; c < dof; ++c) {
4899         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
4900 
4901         for (m = 0; m < joinSize; ++m) {
4902           if (point == join[i][m]) {
4903             join[1 - i][newJoinSize++] = point;
4904             break;
4905           }
4906         }
4907       }
4908       joinSize = newJoinSize;
4909       i        = 1 - i;
4910     }
4911     if (joinSize) break;
4912   }
4913   *numCoveredPoints = joinSize;
4914   *coveredPoints    = join[i];
4915   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4916   PetscCall(PetscFree(closures));
4917   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4918   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
4919   PetscFunctionReturn(PETSC_SUCCESS);
4920 }
4921 
4922 /*@C
4923   DMPlexGetMeet - Get an array for the meet of the set of points
4924 
4925   Not Collective
4926 
4927   Input Parameters:
4928 + dm        - The `DMPLEX` object
4929 . numPoints - The number of input points for the meet
4930 - points    - The input points, of length `numPoints`
4931 
4932   Output Parameters:
4933 + numCoveringPoints - The number of points in the meet
4934 - coveringPoints    - The points in the meet, of length `numCoveringPoints`
4935 
4936   Level: intermediate
4937 
4938   Note:
4939   Currently, this is restricted to a single level meet
4940 
4941   Fortran Notes:
4942   `coveringPoints` must be declared with
4943 .vb
4944   PetscInt, pointer :: coveringPoints(:)
4945 .ve
4946 
4947   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4948 
4949 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4950 @*/
4951 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt *coveringPoints[])
4952 {
4953   DM_Plex  *mesh = (DM_Plex *)dm->data;
4954   PetscInt *meet[2];
4955   PetscInt  meetSize, i = 0;
4956   PetscInt  dof, off, p, c, m;
4957   PetscInt  maxConeSize;
4958 
4959   PetscFunctionBegin;
4960   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4961   PetscAssertPointer(points, 3);
4962   PetscAssertPointer(numCoveringPoints, 4);
4963   PetscAssertPointer(coveringPoints, 5);
4964   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4965   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4966   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4967   /* Copy in cone of first point */
4968   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4969   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4970   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
4971   /* Check each successive cone */
4972   for (p = 1; p < numPoints; ++p) {
4973     PetscInt newMeetSize = 0;
4974 
4975     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4976     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4977     for (c = 0; c < dof; ++c) {
4978       const PetscInt point = mesh->cones[off + c];
4979 
4980       for (m = 0; m < meetSize; ++m) {
4981         if (point == meet[i][m]) {
4982           meet[1 - i][newMeetSize++] = point;
4983           break;
4984         }
4985       }
4986     }
4987     meetSize = newMeetSize;
4988     i        = 1 - i;
4989   }
4990   *numCoveringPoints = meetSize;
4991   *coveringPoints    = meet[i];
4992   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
4993   PetscFunctionReturn(PETSC_SUCCESS);
4994 }
4995 
4996 /*@C
4997   DMPlexRestoreMeet - Restore an array for the meet of the set of points obtained with `DMPlexGetMeet()`
4998 
4999   Not Collective
5000 
5001   Input Parameters:
5002 + dm        - The `DMPLEX` object
5003 . numPoints - The number of input points for the meet
5004 - points    - The input points
5005 
5006   Output Parameters:
5007 + numCoveredPoints - The number of points in the meet
5008 - coveredPoints    - The points in the meet
5009 
5010   Level: intermediate
5011 
5012   Fortran Notes:
5013   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
5014 
5015 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
5016 @*/
5017 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
5018 {
5019   PetscFunctionBegin;
5020   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5021   if (points) PetscAssertPointer(points, 3);
5022   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
5023   PetscAssertPointer(coveredPoints, 5);
5024   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
5025   if (numCoveredPoints) *numCoveredPoints = 0;
5026   PetscFunctionReturn(PETSC_SUCCESS);
5027 }
5028 
5029 /*@C
5030   DMPlexGetFullMeet - Get an array for the meet of the set of points
5031 
5032   Not Collective
5033 
5034   Input Parameters:
5035 + dm        - The `DMPLEX` object
5036 . numPoints - The number of input points for the meet
5037 - points    - The input points, of length  `numPoints`
5038 
5039   Output Parameters:
5040 + numCoveredPoints - The number of points in the meet
5041 - coveredPoints    - The points in the meet, of length  `numCoveredPoints`
5042 
5043   Level: intermediate
5044 
5045   Fortran Notes:
5046   `points` and `coveredPoints` must be declared with
5047 .vb
5048   PetscInt, pointer :: points(:)
5049   PetscInt, pointer :: coveredPoints(:)
5050 .ve
5051 
5052   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
5053 
5054 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
5055 @*/
5056 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
5057 {
5058   PetscInt *offsets, **closures;
5059   PetscInt *meet[2];
5060   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
5061   PetscInt  p, h, c, m, mc;
5062 
5063   PetscFunctionBegin;
5064   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5065   PetscAssertPointer(points, 3);
5066   PetscAssertPointer(numCoveredPoints, 4);
5067   PetscAssertPointer(coveredPoints, 5);
5068 
5069   PetscCall(DMPlexGetDepth(dm, &height));
5070   PetscCall(PetscMalloc1(numPoints, &closures));
5071   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5072   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
5073   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
5074   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
5075   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
5076 
5077   for (p = 0; p < numPoints; ++p) {
5078     PetscInt closureSize;
5079 
5080     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
5081 
5082     offsets[p * (height + 2) + 0] = 0;
5083     for (h = 0; h < height + 1; ++h) {
5084       PetscInt pStart, pEnd, i;
5085 
5086       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
5087       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
5088         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
5089           offsets[p * (height + 2) + h + 1] = i;
5090           break;
5091         }
5092       }
5093       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
5094     }
5095     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);
5096   }
5097   for (h = 0; h < height + 1; ++h) {
5098     PetscInt dof;
5099 
5100     /* Copy in cone of first point */
5101     dof = offsets[h + 1] - offsets[h];
5102     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
5103     /* Check each successive cone */
5104     for (p = 1; p < numPoints && meetSize; ++p) {
5105       PetscInt newMeetSize = 0;
5106 
5107       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
5108       for (c = 0; c < dof; ++c) {
5109         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
5110 
5111         for (m = 0; m < meetSize; ++m) {
5112           if (point == meet[i][m]) {
5113             meet[1 - i][newMeetSize++] = point;
5114             break;
5115           }
5116         }
5117       }
5118       meetSize = newMeetSize;
5119       i        = 1 - i;
5120     }
5121     if (meetSize) break;
5122   }
5123   *numCoveredPoints = meetSize;
5124   *coveredPoints    = meet[i];
5125   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
5126   PetscCall(PetscFree(closures));
5127   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5128   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
5129   PetscFunctionReturn(PETSC_SUCCESS);
5130 }
5131 
5132 /*@
5133   DMPlexEqual - Determine if two `DM` have the same topology
5134 
5135   Not Collective
5136 
5137   Input Parameters:
5138 + dmA - A `DMPLEX` object
5139 - dmB - A `DMPLEX` object
5140 
5141   Output Parameter:
5142 . equal - `PETSC_TRUE` if the topologies are identical
5143 
5144   Level: intermediate
5145 
5146   Note:
5147   We are not solving graph isomorphism, so we do not permute.
5148 
5149 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5150 @*/
5151 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
5152 {
5153   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
5154 
5155   PetscFunctionBegin;
5156   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
5157   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
5158   PetscAssertPointer(equal, 3);
5159 
5160   *equal = PETSC_FALSE;
5161   PetscCall(DMPlexGetDepth(dmA, &depth));
5162   PetscCall(DMPlexGetDepth(dmB, &depthB));
5163   if (depth != depthB) PetscFunctionReturn(PETSC_SUCCESS);
5164   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
5165   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
5166   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(PETSC_SUCCESS);
5167   for (p = pStart; p < pEnd; ++p) {
5168     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
5169     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
5170 
5171     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
5172     PetscCall(DMPlexGetCone(dmA, p, &cone));
5173     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
5174     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
5175     PetscCall(DMPlexGetCone(dmB, p, &coneB));
5176     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
5177     if (coneSize != coneSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5178     for (c = 0; c < coneSize; ++c) {
5179       if (cone[c] != coneB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5180       if (ornt[c] != orntB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5181     }
5182     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
5183     PetscCall(DMPlexGetSupport(dmA, p, &support));
5184     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
5185     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
5186     if (supportSize != supportSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5187     for (s = 0; s < supportSize; ++s) {
5188       if (support[s] != supportB[s]) PetscFunctionReturn(PETSC_SUCCESS);
5189     }
5190   }
5191   *equal = PETSC_TRUE;
5192   PetscFunctionReturn(PETSC_SUCCESS);
5193 }
5194 
5195 /*@
5196   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
5197 
5198   Not Collective
5199 
5200   Input Parameters:
5201 + dm         - The `DMPLEX`
5202 . cellDim    - The cell dimension
5203 - numCorners - The number of vertices on a cell
5204 
5205   Output Parameter:
5206 . numFaceVertices - The number of vertices on a face
5207 
5208   Level: developer
5209 
5210   Note:
5211   Of course this can only work for a restricted set of symmetric shapes
5212 
5213 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5214 @*/
5215 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
5216 {
5217   MPI_Comm comm;
5218 
5219   PetscFunctionBegin;
5220   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
5221   PetscAssertPointer(numFaceVertices, 4);
5222   switch (cellDim) {
5223   case 0:
5224     *numFaceVertices = 0;
5225     break;
5226   case 1:
5227     *numFaceVertices = 1;
5228     break;
5229   case 2:
5230     switch (numCorners) {
5231     case 3:                 /* triangle */
5232       *numFaceVertices = 2; /* Edge has 2 vertices */
5233       break;
5234     case 4:                 /* quadrilateral */
5235       *numFaceVertices = 2; /* Edge has 2 vertices */
5236       break;
5237     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
5238       *numFaceVertices = 3; /* Edge has 3 vertices */
5239       break;
5240     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
5241       *numFaceVertices = 3; /* Edge has 3 vertices */
5242       break;
5243     default:
5244       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5245     }
5246     break;
5247   case 3:
5248     switch (numCorners) {
5249     case 4:                 /* tetradehdron */
5250       *numFaceVertices = 3; /* Face has 3 vertices */
5251       break;
5252     case 6:                 /* tet cohesive cells */
5253       *numFaceVertices = 4; /* Face has 4 vertices */
5254       break;
5255     case 8:                 /* hexahedron */
5256       *numFaceVertices = 4; /* Face has 4 vertices */
5257       break;
5258     case 9:                 /* tet cohesive Lagrange cells */
5259       *numFaceVertices = 6; /* Face has 6 vertices */
5260       break;
5261     case 10:                /* quadratic tetrahedron */
5262       *numFaceVertices = 6; /* Face has 6 vertices */
5263       break;
5264     case 12:                /* hex cohesive Lagrange cells */
5265       *numFaceVertices = 6; /* Face has 6 vertices */
5266       break;
5267     case 18:                /* quadratic tet cohesive Lagrange cells */
5268       *numFaceVertices = 6; /* Face has 6 vertices */
5269       break;
5270     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
5271       *numFaceVertices = 9; /* Face has 9 vertices */
5272       break;
5273     default:
5274       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5275     }
5276     break;
5277   default:
5278     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
5279   }
5280   PetscFunctionReturn(PETSC_SUCCESS);
5281 }
5282 
5283 /*@
5284   DMPlexGetDepthLabel - Get the `DMLabel` recording the depth of each point
5285 
5286   Not Collective
5287 
5288   Input Parameter:
5289 . dm - The `DMPLEX` object
5290 
5291   Output Parameter:
5292 . depthLabel - The `DMLabel` recording point depth
5293 
5294   Level: developer
5295 
5296 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
5297 @*/
5298 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
5299 {
5300   PetscFunctionBegin;
5301   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5302   PetscAssertPointer(depthLabel, 2);
5303   *depthLabel = dm->depthLabel;
5304   PetscFunctionReturn(PETSC_SUCCESS);
5305 }
5306 
5307 /*@
5308   DMPlexGetDepth - Get the depth of the DAG representing this mesh
5309 
5310   Not Collective
5311 
5312   Input Parameter:
5313 . dm - The `DMPLEX` object
5314 
5315   Output Parameter:
5316 . depth - The number of strata (breadth first levels) in the DAG
5317 
5318   Level: developer
5319 
5320   Notes:
5321   This returns maximum of point depths over all points, i.e. maximum value of the label returned by `DMPlexGetDepthLabel()`.
5322 
5323   The point depth is described more in detail in `DMPlexGetDepthStratum()`.
5324 
5325   An empty mesh gives -1.
5326 
5327 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
5328 @*/
5329 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
5330 {
5331   DM_Plex *mesh = (DM_Plex *)dm->data;
5332   DMLabel  label;
5333   PetscInt d = -1;
5334 
5335   PetscFunctionBegin;
5336   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5337   PetscAssertPointer(depth, 2);
5338   if (mesh->tr) {
5339     PetscCall(DMPlexTransformGetDepth(mesh->tr, depth));
5340   } else {
5341     PetscCall(DMPlexGetDepthLabel(dm, &label));
5342     // Allow missing depths
5343     if (label) PetscCall(DMLabelGetValueBounds(label, NULL, &d));
5344     *depth = d;
5345   }
5346   PetscFunctionReturn(PETSC_SUCCESS);
5347 }
5348 
5349 /*@
5350   DMPlexGetDepthStratum - Get the bounds [`start`, `end`) for all points at a certain depth.
5351 
5352   Not Collective
5353 
5354   Input Parameters:
5355 + dm    - The `DMPLEX` object
5356 - depth - The requested depth
5357 
5358   Output Parameters:
5359 + start - The first point at this `depth`
5360 - end   - One beyond the last point at this `depth`
5361 
5362   Level: developer
5363 
5364   Notes:
5365   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
5366   often "vertices".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then depth stratum 1 contains the next
5367   higher dimension, e.g., "edges".
5368 
5369 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetHeightStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
5370 @*/
5371 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
5372 {
5373   DM_Plex *mesh = (DM_Plex *)dm->data;
5374   DMLabel  label;
5375   PetscInt pStart, pEnd;
5376 
5377   PetscFunctionBegin;
5378   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5379   if (start) {
5380     PetscAssertPointer(start, 3);
5381     *start = 0;
5382   }
5383   if (end) {
5384     PetscAssertPointer(end, 4);
5385     *end = 0;
5386   }
5387   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5388   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5389   if (depth < 0) {
5390     if (start) *start = pStart;
5391     if (end) *end = pEnd;
5392     PetscFunctionReturn(PETSC_SUCCESS);
5393   }
5394   if (mesh->tr) {
5395     PetscCall(DMPlexTransformGetDepthStratum(mesh->tr, depth, start, end));
5396   } else {
5397     PetscCall(DMPlexGetDepthLabel(dm, &label));
5398     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5399     PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
5400   }
5401   PetscFunctionReturn(PETSC_SUCCESS);
5402 }
5403 
5404 /*@
5405   DMPlexGetHeightStratum - Get the bounds [`start`, `end`) for all points at a certain height.
5406 
5407   Not Collective
5408 
5409   Input Parameters:
5410 + dm     - The `DMPLEX` object
5411 - height - The requested height
5412 
5413   Output Parameters:
5414 + start - The first point at this `height`
5415 - end   - One beyond the last point at this `height`
5416 
5417   Level: developer
5418 
5419   Notes:
5420   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
5421   points, often called "cells" or "elements".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then height
5422   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
5423 
5424 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5425 @*/
5426 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
5427 {
5428   DMLabel  label;
5429   PetscInt depth, pStart, pEnd;
5430 
5431   PetscFunctionBegin;
5432   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5433   if (start) {
5434     PetscAssertPointer(start, 3);
5435     *start = 0;
5436   }
5437   if (end) {
5438     PetscAssertPointer(end, 4);
5439     *end = 0;
5440   }
5441   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5442   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5443   if (height < 0) {
5444     if (start) *start = pStart;
5445     if (end) *end = pEnd;
5446     PetscFunctionReturn(PETSC_SUCCESS);
5447   }
5448   PetscCall(DMPlexGetDepthLabel(dm, &label));
5449   if (label) PetscCall(DMLabelGetNumValues(label, &depth));
5450   else PetscCall(DMGetDimension(dm, &depth));
5451   PetscCheck(depth >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Depth not yet computed");
5452   PetscCall(DMPlexGetDepthStratum(dm, depth - 1 - height, start, end));
5453   PetscFunctionReturn(PETSC_SUCCESS);
5454 }
5455 
5456 /*@
5457   DMPlexGetPointDepth - Get the `depth` of a given point
5458 
5459   Not Collective
5460 
5461   Input Parameters:
5462 + dm    - The `DMPLEX` object
5463 - point - The point
5464 
5465   Output Parameter:
5466 . depth - The depth of the `point`
5467 
5468   Level: intermediate
5469 
5470 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5471 @*/
5472 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
5473 {
5474   PetscFunctionBegin;
5475   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5476   PetscAssertPointer(depth, 3);
5477   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
5478   PetscFunctionReturn(PETSC_SUCCESS);
5479 }
5480 
5481 /*@
5482   DMPlexGetPointHeight - Get the `height` of a given point
5483 
5484   Not Collective
5485 
5486   Input Parameters:
5487 + dm    - The `DMPLEX` object
5488 - point - The point
5489 
5490   Output Parameter:
5491 . height - The height of the `point`
5492 
5493   Level: intermediate
5494 
5495 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
5496 @*/
5497 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5498 {
5499   PetscInt n, pDepth;
5500 
5501   PetscFunctionBegin;
5502   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5503   PetscAssertPointer(height, 3);
5504   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5505   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5506   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5507   PetscFunctionReturn(PETSC_SUCCESS);
5508 }
5509 
5510 /*@
5511   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5512 
5513   Not Collective
5514 
5515   Input Parameter:
5516 . dm - The `DMPLEX` object
5517 
5518   Output Parameter:
5519 . celltypeLabel - The `DMLabel` recording cell polytope type
5520 
5521   Level: developer
5522 
5523   Note:
5524   This function will trigger automatica computation of cell types. This can be disabled by calling
5525   `DMCreateLabel`(dm, "celltype") beforehand.
5526 
5527 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5528 @*/
5529 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5530 {
5531   PetscFunctionBegin;
5532   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5533   PetscAssertPointer(celltypeLabel, 2);
5534   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5535   *celltypeLabel = dm->celltypeLabel;
5536   PetscFunctionReturn(PETSC_SUCCESS);
5537 }
5538 
5539 /*@
5540   DMPlexGetCellType - Get the polytope type of a given cell
5541 
5542   Not Collective
5543 
5544   Input Parameters:
5545 + dm   - The `DMPLEX` object
5546 - cell - The cell
5547 
5548   Output Parameter:
5549 . celltype - The polytope type of the cell
5550 
5551   Level: intermediate
5552 
5553 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeType`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5554 @*/
5555 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5556 {
5557   DM_Plex *mesh = (DM_Plex *)dm->data;
5558   DMLabel  label;
5559   PetscInt ct;
5560 
5561   PetscFunctionBegin;
5562   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5563   PetscAssertPointer(celltype, 3);
5564   if (mesh->tr) {
5565     PetscCall(DMPlexTransformGetCellType(mesh->tr, cell, celltype));
5566   } else {
5567     PetscInt pStart, pEnd;
5568 
5569     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, NULL));
5570     if (!mesh->cellTypes) { /* XXX remove? optimize? */
5571       PetscCall(PetscSectionGetChart(mesh->coneSection, NULL, &pEnd));
5572       PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5573       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5574       for (PetscInt p = pStart; p < pEnd; p++) {
5575         PetscCall(DMLabelGetValue(label, p, &ct));
5576         mesh->cellTypes[p - pStart].value_as_uint8 = (DMPolytopeType)ct;
5577       }
5578     }
5579     *celltype = (DMPolytopeType)mesh->cellTypes[cell - pStart].value_as_uint8;
5580     if (PetscDefined(USE_DEBUG)) {
5581       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5582       PetscCall(DMLabelGetValue(label, cell, &ct));
5583       PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5584       PetscCheck(ct == (PetscInt)*celltype, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid cellType for %" PetscInt_FMT ": %d != %" PetscInt_FMT, cell, (int)*celltype, ct);
5585     }
5586   }
5587   PetscFunctionReturn(PETSC_SUCCESS);
5588 }
5589 
5590 /*@
5591   DMPlexSetCellType - Set the polytope type of a given cell
5592 
5593   Not Collective
5594 
5595   Input Parameters:
5596 + dm       - The `DMPLEX` object
5597 . cell     - The cell
5598 - celltype - The polytope type of the cell
5599 
5600   Level: advanced
5601 
5602   Note:
5603   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5604   is executed. This function will override the computed type. However, if automatic classification will not succeed
5605   and a user wants to manually specify all types, the classification must be disabled by calling
5606   DMCreateLabel(dm, "celltype") before getting or setting any cell types.
5607 
5608 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5609 @*/
5610 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5611 {
5612   DM_Plex *mesh = (DM_Plex *)dm->data;
5613   DMLabel  label;
5614   PetscInt pStart, pEnd;
5615 
5616   PetscFunctionBegin;
5617   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5618   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
5619   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5620   PetscCall(DMLabelSetValue(label, cell, celltype));
5621   if (!mesh->cellTypes) PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5622   mesh->cellTypes[cell - pStart].value_as_uint8 = celltype;
5623   PetscFunctionReturn(PETSC_SUCCESS);
5624 }
5625 
5626 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5627 {
5628   PetscSection section;
5629   PetscInt     maxHeight;
5630   const char  *prefix;
5631 
5632   PetscFunctionBegin;
5633   PetscCall(DMClone(dm, cdm));
5634   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5635   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5636   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5637   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5638   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5639   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5640   PetscCall(DMSetLocalSection(*cdm, section));
5641   PetscCall(PetscSectionDestroy(&section));
5642 
5643   PetscCall(DMSetNumFields(*cdm, 1));
5644   PetscCall(DMCreateDS(*cdm));
5645   (*cdm)->cloneOpts = PETSC_TRUE;
5646   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5647   PetscFunctionReturn(PETSC_SUCCESS);
5648 }
5649 
5650 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5651 {
5652   Vec coordsLocal, cellCoordsLocal;
5653   DM  coordsDM, cellCoordsDM;
5654 
5655   PetscFunctionBegin;
5656   *field = NULL;
5657   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5658   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5659   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5660   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5661   if (coordsLocal && coordsDM) {
5662     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5663     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5664   }
5665   PetscFunctionReturn(PETSC_SUCCESS);
5666 }
5667 
5668 /*@
5669   DMPlexGetConeSection - Return a section which describes the layout of cone data
5670 
5671   Not Collective
5672 
5673   Input Parameter:
5674 . dm - The `DMPLEX` object
5675 
5676   Output Parameter:
5677 . section - The `PetscSection` object
5678 
5679   Level: developer
5680 
5681 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5682 @*/
5683 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5684 {
5685   DM_Plex *mesh = (DM_Plex *)dm->data;
5686 
5687   PetscFunctionBegin;
5688   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5689   if (section) *section = mesh->coneSection;
5690   PetscFunctionReturn(PETSC_SUCCESS);
5691 }
5692 
5693 /*@
5694   DMPlexGetSupportSection - Return a section which describes the layout of support data
5695 
5696   Not Collective
5697 
5698   Input Parameter:
5699 . dm - The `DMPLEX` object
5700 
5701   Output Parameter:
5702 . section - The `PetscSection` object
5703 
5704   Level: developer
5705 
5706 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5707 @*/
5708 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5709 {
5710   DM_Plex *mesh = (DM_Plex *)dm->data;
5711 
5712   PetscFunctionBegin;
5713   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5714   if (section) *section = mesh->supportSection;
5715   PetscFunctionReturn(PETSC_SUCCESS);
5716 }
5717 
5718 /*@C
5719   DMPlexGetCones - Return cone data
5720 
5721   Not Collective
5722 
5723   Input Parameter:
5724 . dm - The `DMPLEX` object
5725 
5726   Output Parameter:
5727 . cones - The cone for each point
5728 
5729   Level: developer
5730 
5731 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5732 @*/
5733 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5734 {
5735   DM_Plex *mesh = (DM_Plex *)dm->data;
5736 
5737   PetscFunctionBegin;
5738   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5739   if (cones) *cones = mesh->cones;
5740   PetscFunctionReturn(PETSC_SUCCESS);
5741 }
5742 
5743 /*@C
5744   DMPlexGetConeOrientations - Return cone orientation data
5745 
5746   Not Collective
5747 
5748   Input Parameter:
5749 . dm - The `DMPLEX` object
5750 
5751   Output Parameter:
5752 . coneOrientations - The array of cone orientations for all points
5753 
5754   Level: developer
5755 
5756   Notes:
5757   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points
5758   as returned by `DMPlexGetConeOrientation()`.
5759 
5760   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5761 
5762 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5763 @*/
5764 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5765 {
5766   DM_Plex *mesh = (DM_Plex *)dm->data;
5767 
5768   PetscFunctionBegin;
5769   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5770   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5771   PetscFunctionReturn(PETSC_SUCCESS);
5772 }
5773 
5774 /******************************** FEM Support **********************************/
5775 
5776 PetscErrorCode DMPlexGetAllCells_Internal(DM plex, IS *cellIS)
5777 {
5778   PetscInt depth;
5779 
5780   PetscFunctionBegin;
5781   PetscCall(DMPlexGetDepth(plex, &depth));
5782   PetscCall(DMGetStratumIS(plex, "dim", depth, cellIS));
5783   if (!*cellIS) PetscCall(DMGetStratumIS(plex, "depth", depth, cellIS));
5784   PetscFunctionReturn(PETSC_SUCCESS);
5785 }
5786 
5787 PetscErrorCode DMPlexGetAllFaces_Internal(DM plex, IS *faceIS)
5788 {
5789   PetscInt depth;
5790 
5791   PetscFunctionBegin;
5792   PetscCall(DMPlexGetDepth(plex, &depth));
5793   PetscCall(DMGetStratumIS(plex, "dim", depth - 1, faceIS));
5794   if (!*faceIS) PetscCall(DMGetStratumIS(plex, "depth", depth - 1, faceIS));
5795   PetscFunctionReturn(PETSC_SUCCESS);
5796 }
5797 
5798 /*
5799  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5800  representing a line in the section.
5801 */
5802 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(DM dm, PetscSection section, PetscInt field, PetscInt line, PetscInt *Nc, PetscInt *k, PetscBool *continuous, PetscBool *tensor)
5803 {
5804   PetscObject  obj;
5805   PetscClassId id;
5806   PetscFE      fe = NULL;
5807 
5808   PetscFunctionBeginHot;
5809   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5810   PetscCall(DMGetField(dm, field, NULL, &obj));
5811   PetscCall(PetscObjectGetClassId(obj, &id));
5812   if (id == PETSCFE_CLASSID) fe = (PetscFE)obj;
5813 
5814   if (!fe) {
5815     /* Assume the full interpolated mesh is in the chart; lines in particular */
5816     /* An order k SEM disc has k-1 dofs on an edge */
5817     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5818     *k = *k / *Nc + 1;
5819   } else {
5820     PetscInt       dual_space_size, dim;
5821     PetscDualSpace dsp;
5822 
5823     PetscCall(DMGetDimension(dm, &dim));
5824     PetscCall(PetscFEGetDualSpace(fe, &dsp));
5825     PetscCall(PetscDualSpaceGetDimension(dsp, &dual_space_size));
5826     *k = (PetscInt)PetscCeilReal(PetscPowReal(dual_space_size / *Nc, 1.0 / dim)) - 1;
5827     PetscCall(PetscDualSpaceLagrangeGetContinuity(dsp, continuous));
5828     PetscCall(PetscDualSpaceLagrangeGetTensor(dsp, tensor));
5829   }
5830   PetscFunctionReturn(PETSC_SUCCESS);
5831 }
5832 
5833 static PetscErrorCode GetFieldSize_Private(PetscInt dim, PetscInt k, PetscBool tensor, PetscInt *dof)
5834 {
5835   PetscFunctionBeginHot;
5836   if (tensor) {
5837     *dof = PetscPowInt(k + 1, dim);
5838   } else {
5839     switch (dim) {
5840     case 1:
5841       *dof = k + 1;
5842       break;
5843     case 2:
5844       *dof = ((k + 1) * (k + 2)) / 2;
5845       break;
5846     case 3:
5847       *dof = ((k + 1) * (k + 2) * (k + 3)) / 6;
5848       break;
5849     default:
5850       *dof = 0;
5851     }
5852   }
5853   PetscFunctionReturn(PETSC_SUCCESS);
5854 }
5855 
5856 /*@
5857   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5858   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5859   section provided (or the section of the `DM`).
5860 
5861   Input Parameters:
5862 + dm      - The `DM`
5863 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or `PETSC_DETERMINE`
5864 - section - The `PetscSection` to reorder, or `NULL` for the default section
5865 
5866   Example:
5867   A typical interpolated single-quad mesh might order points as
5868 .vb
5869   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5870 
5871   v4 -- e6 -- v3
5872   |           |
5873   e7    c0    e8
5874   |           |
5875   v1 -- e5 -- v2
5876 .ve
5877 
5878   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5879   dofs in the order of points, e.g.,
5880 .vb
5881     c0 -> [0,1,2,3]
5882     v1 -> [4]
5883     ...
5884     e5 -> [8, 9]
5885 .ve
5886 
5887   which corresponds to the dofs
5888 .vb
5889     6   10  11  7
5890     13  2   3   15
5891     12  0   1   14
5892     4   8   9   5
5893 .ve
5894 
5895   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5896 .vb
5897   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5898 .ve
5899 
5900   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5901 .vb
5902    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5903 .ve
5904 
5905   Level: developer
5906 
5907   Notes:
5908   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5909   degree of the basis.
5910 
5911   This is required to run with libCEED.
5912 
5913 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5914 @*/
5915 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5916 {
5917   DMLabel   label;
5918   PetscInt  dim, depth = -1, eStart = -1, Nf;
5919   PetscBool continuous = PETSC_TRUE, tensor = PETSC_TRUE;
5920 
5921   PetscFunctionBegin;
5922   PetscCall(DMGetDimension(dm, &dim));
5923   if (dim < 1) PetscFunctionReturn(PETSC_SUCCESS);
5924   if (point < 0) {
5925     PetscInt sStart, sEnd;
5926 
5927     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5928     point = sEnd - sStart ? sStart : point;
5929   }
5930   PetscCall(DMPlexGetDepthLabel(dm, &label));
5931   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5932   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5933   if (depth == 1) {
5934     eStart = point;
5935   } else if (depth == dim) {
5936     const PetscInt *cone;
5937 
5938     PetscCall(DMPlexGetCone(dm, point, &cone));
5939     if (dim == 2) eStart = cone[0];
5940     else if (dim == 3) {
5941       const PetscInt *cone2;
5942       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5943       eStart = cone2[0];
5944     } 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);
5945   } 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);
5946 
5947   PetscCall(PetscSectionGetNumFields(section, &Nf));
5948   for (PetscInt d = 1; d <= dim; d++) {
5949     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5950     PetscInt *perm;
5951 
5952     for (f = 0; f < Nf; ++f) {
5953       PetscInt dof;
5954 
5955       PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5956       PetscCheck(dim == 1 || tensor || !continuous, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Continuous field %" PetscInt_FMT " must have a tensor product discretization", f);
5957       if (!continuous && d < dim) continue;
5958       PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
5959       size += dof * Nc;
5960     }
5961     PetscCall(PetscMalloc1(size, &perm));
5962     for (f = 0; f < Nf; ++f) {
5963       switch (d) {
5964       case 1:
5965         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5966         if (!continuous && d < dim) continue;
5967         /*
5968          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5969          We want              [ vtx0; edge of length k-1; vtx1 ]
5970          */
5971         if (continuous) {
5972           for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
5973           for (i = 0; i < k - 1; i++)
5974             for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
5975           for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
5976           foffset = offset;
5977         } else {
5978           PetscInt dof;
5979 
5980           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
5981           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
5982           foffset = offset;
5983         }
5984         break;
5985       case 2:
5986         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5987         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5988         if (!continuous && d < dim) continue;
5989         /* The SEM order is
5990 
5991          v_lb, {e_b}, v_rb,
5992          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5993          v_lt, reverse {e_t}, v_rt
5994          */
5995         if (continuous) {
5996           const PetscInt of   = 0;
5997           const PetscInt oeb  = of + PetscSqr(k - 1);
5998           const PetscInt oer  = oeb + (k - 1);
5999           const PetscInt oet  = oer + (k - 1);
6000           const PetscInt oel  = oet + (k - 1);
6001           const PetscInt ovlb = oel + (k - 1);
6002           const PetscInt ovrb = ovlb + 1;
6003           const PetscInt ovrt = ovrb + 1;
6004           const PetscInt ovlt = ovrt + 1;
6005           PetscInt       o;
6006 
6007           /* bottom */
6008           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
6009           for (o = oeb; o < oer; ++o)
6010             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6011           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
6012           /* middle */
6013           for (i = 0; i < k - 1; ++i) {
6014             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
6015             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
6016               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6017             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
6018           }
6019           /* top */
6020           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
6021           for (o = oel - 1; o >= oet; --o)
6022             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6023           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
6024           foffset = offset;
6025         } else {
6026           PetscInt dof;
6027 
6028           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6029           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6030           foffset = offset;
6031         }
6032         break;
6033       case 3:
6034         /* The original hex closure is
6035 
6036          {c,
6037          f_b, f_t, f_f, f_b, f_r, f_l,
6038          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
6039          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
6040          */
6041         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6042         if (!continuous && d < dim) continue;
6043         /* The SEM order is
6044          Bottom Slice
6045          v_blf, {e^{(k-1)-n}_bf}, v_brf,
6046          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
6047          v_blb, {e_bb}, v_brb,
6048 
6049          Middle Slice (j)
6050          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
6051          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
6052          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
6053 
6054          Top Slice
6055          v_tlf, {e_tf}, v_trf,
6056          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
6057          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
6058          */
6059         if (continuous) {
6060           const PetscInt oc    = 0;
6061           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
6062           const PetscInt oft   = ofb + PetscSqr(k - 1);
6063           const PetscInt off   = oft + PetscSqr(k - 1);
6064           const PetscInt ofk   = off + PetscSqr(k - 1);
6065           const PetscInt ofr   = ofk + PetscSqr(k - 1);
6066           const PetscInt ofl   = ofr + PetscSqr(k - 1);
6067           const PetscInt oebl  = ofl + PetscSqr(k - 1);
6068           const PetscInt oebb  = oebl + (k - 1);
6069           const PetscInt oebr  = oebb + (k - 1);
6070           const PetscInt oebf  = oebr + (k - 1);
6071           const PetscInt oetf  = oebf + (k - 1);
6072           const PetscInt oetr  = oetf + (k - 1);
6073           const PetscInt oetb  = oetr + (k - 1);
6074           const PetscInt oetl  = oetb + (k - 1);
6075           const PetscInt oerf  = oetl + (k - 1);
6076           const PetscInt oelf  = oerf + (k - 1);
6077           const PetscInt oelb  = oelf + (k - 1);
6078           const PetscInt oerb  = oelb + (k - 1);
6079           const PetscInt ovblf = oerb + (k - 1);
6080           const PetscInt ovblb = ovblf + 1;
6081           const PetscInt ovbrb = ovblb + 1;
6082           const PetscInt ovbrf = ovbrb + 1;
6083           const PetscInt ovtlf = ovbrf + 1;
6084           const PetscInt ovtrf = ovtlf + 1;
6085           const PetscInt ovtrb = ovtrf + 1;
6086           const PetscInt ovtlb = ovtrb + 1;
6087           PetscInt       o, n;
6088 
6089           /* Bottom Slice */
6090           /*   bottom */
6091           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
6092           for (o = oetf - 1; o >= oebf; --o)
6093             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6094           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
6095           /*   middle */
6096           for (i = 0; i < k - 1; ++i) {
6097             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
6098             for (n = 0; n < k - 1; ++n) {
6099               o = ofb + n * (k - 1) + i;
6100               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6101             }
6102             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
6103           }
6104           /*   top */
6105           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
6106           for (o = oebb; o < oebr; ++o)
6107             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6108           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
6109 
6110           /* Middle Slice */
6111           for (j = 0; j < k - 1; ++j) {
6112             /*   bottom */
6113             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
6114             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
6115               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6116             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
6117             /*   middle */
6118             for (i = 0; i < k - 1; ++i) {
6119               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
6120               for (n = 0; n < k - 1; ++n)
6121                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
6122               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
6123             }
6124             /*   top */
6125             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
6126             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
6127               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6128             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
6129           }
6130 
6131           /* Top Slice */
6132           /*   bottom */
6133           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
6134           for (o = oetf; o < oetr; ++o)
6135             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6136           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
6137           /*   middle */
6138           for (i = 0; i < k - 1; ++i) {
6139             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
6140             for (n = 0; n < k - 1; ++n)
6141               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
6142             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
6143           }
6144           /*   top */
6145           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
6146           for (o = oetl - 1; o >= oetb; --o)
6147             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6148           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
6149 
6150           foffset = offset;
6151         } else {
6152           PetscInt dof;
6153 
6154           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6155           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6156           foffset = offset;
6157         }
6158         break;
6159       default:
6160         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
6161       }
6162     }
6163     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
6164     /* Check permutation */
6165     {
6166       PetscInt *check;
6167 
6168       PetscCall(PetscMalloc1(size, &check));
6169       for (i = 0; i < size; ++i) {
6170         check[i] = -1;
6171         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
6172       }
6173       for (i = 0; i < size; ++i) check[perm[i]] = i;
6174       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
6175       PetscCall(PetscFree(check));
6176     }
6177     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
6178     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
6179       PetscInt *loc_perm;
6180       PetscCall(PetscMalloc1(size * 2, &loc_perm));
6181       for (PetscInt i = 0; i < size; i++) {
6182         loc_perm[i]        = perm[i];
6183         loc_perm[size + i] = size + perm[i];
6184       }
6185       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
6186     }
6187   }
6188   PetscFunctionReturn(PETSC_SUCCESS);
6189 }
6190 
6191 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
6192 {
6193   PetscDS  prob;
6194   PetscInt depth, Nf, h;
6195   DMLabel  label;
6196 
6197   PetscFunctionBeginHot;
6198   PetscCall(DMGetDS(dm, &prob));
6199   Nf      = prob->Nf;
6200   label   = dm->depthLabel;
6201   *dspace = NULL;
6202   if (field < Nf) {
6203     PetscObject disc = prob->disc[field];
6204 
6205     if (disc->classid == PETSCFE_CLASSID) {
6206       PetscDualSpace dsp;
6207 
6208       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
6209       PetscCall(DMLabelGetNumValues(label, &depth));
6210       PetscCall(DMLabelGetValue(label, point, &h));
6211       h = depth - 1 - h;
6212       if (h) {
6213         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
6214       } else {
6215         *dspace = dsp;
6216       }
6217     }
6218   }
6219   PetscFunctionReturn(PETSC_SUCCESS);
6220 }
6221 
6222 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6223 {
6224   PetscScalar       *array;
6225   const PetscScalar *vArray;
6226   const PetscInt    *cone, *coneO;
6227   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
6228 
6229   PetscFunctionBeginHot;
6230   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6231   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6232   PetscCall(DMPlexGetCone(dm, point, &cone));
6233   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6234   if (!values || !*values) {
6235     if ((point >= pStart) && (point < pEnd)) {
6236       PetscInt dof;
6237 
6238       PetscCall(PetscSectionGetDof(section, point, &dof));
6239       size += dof;
6240     }
6241     for (p = 0; p < numPoints; ++p) {
6242       const PetscInt cp = cone[p];
6243       PetscInt       dof;
6244 
6245       if ((cp < pStart) || (cp >= pEnd)) continue;
6246       PetscCall(PetscSectionGetDof(section, cp, &dof));
6247       size += dof;
6248     }
6249     if (!values) {
6250       if (csize) *csize = size;
6251       PetscFunctionReturn(PETSC_SUCCESS);
6252     }
6253     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
6254   } else {
6255     array = *values;
6256   }
6257   size = 0;
6258   PetscCall(VecGetArrayRead(v, &vArray));
6259   if ((point >= pStart) && (point < pEnd)) {
6260     PetscInt           dof, off, d;
6261     const PetscScalar *varr;
6262 
6263     PetscCall(PetscSectionGetDof(section, point, &dof));
6264     PetscCall(PetscSectionGetOffset(section, point, &off));
6265     varr = PetscSafePointerPlusOffset(vArray, off);
6266     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6267     size += dof;
6268   }
6269   for (p = 0; p < numPoints; ++p) {
6270     const PetscInt     cp = cone[p];
6271     PetscInt           o  = coneO[p];
6272     PetscInt           dof, off, d;
6273     const PetscScalar *varr;
6274 
6275     if ((cp < pStart) || (cp >= pEnd)) continue;
6276     PetscCall(PetscSectionGetDof(section, cp, &dof));
6277     PetscCall(PetscSectionGetOffset(section, cp, &off));
6278     varr = PetscSafePointerPlusOffset(vArray, off);
6279     if (o >= 0) {
6280       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6281     } else {
6282       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
6283     }
6284     size += dof;
6285   }
6286   PetscCall(VecRestoreArrayRead(v, &vArray));
6287   if (!*values) {
6288     if (csize) *csize = size;
6289     *values = array;
6290   } else {
6291     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6292     *csize = size;
6293   }
6294   PetscFunctionReturn(PETSC_SUCCESS);
6295 }
6296 
6297 /* Compress out points not in the section */
6298 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
6299 {
6300   const PetscInt np = *numPoints;
6301   PetscInt       pStart, pEnd, p, q;
6302 
6303   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6304   for (p = 0, q = 0; p < np; ++p) {
6305     const PetscInt r = points[p * 2];
6306     if ((r >= pStart) && (r < pEnd)) {
6307       points[q * 2]     = r;
6308       points[q * 2 + 1] = points[p * 2 + 1];
6309       ++q;
6310     }
6311   }
6312   *numPoints = q;
6313   return PETSC_SUCCESS;
6314 }
6315 
6316 /* Compressed closure does not apply closure permutation */
6317 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt ornt, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6318 {
6319   const PetscInt *cla = NULL;
6320   PetscInt        np, *pts = NULL;
6321 
6322   PetscFunctionBeginHot;
6323   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
6324   if (!ornt && *clPoints) {
6325     PetscInt dof, off;
6326 
6327     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
6328     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
6329     PetscCall(ISGetIndices(*clPoints, &cla));
6330     np  = dof / 2;
6331     pts = PetscSafePointerPlusOffset((PetscInt *)cla, off);
6332   } else {
6333     PetscCall(DMPlexGetTransitiveClosure_Internal(dm, point, ornt, PETSC_TRUE, &np, &pts));
6334     PetscCall(CompressPoints_Private(section, &np, pts));
6335   }
6336   *numPoints = np;
6337   *points    = pts;
6338   *clp       = cla;
6339   PetscFunctionReturn(PETSC_SUCCESS);
6340 }
6341 
6342 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6343 {
6344   PetscFunctionBeginHot;
6345   if (!*clPoints) {
6346     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
6347   } else {
6348     PetscCall(ISRestoreIndices(*clPoints, clp));
6349   }
6350   *numPoints = 0;
6351   *points    = NULL;
6352   *clSec     = NULL;
6353   *clPoints  = NULL;
6354   *clp       = NULL;
6355   PetscFunctionReturn(PETSC_SUCCESS);
6356 }
6357 
6358 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
6359 {
6360   PetscInt            offset = 0, p;
6361   const PetscInt    **perms  = NULL;
6362   const PetscScalar **flips  = NULL;
6363 
6364   PetscFunctionBeginHot;
6365   *size = 0;
6366   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6367   for (p = 0; p < numPoints; p++) {
6368     const PetscInt     point = points[2 * p];
6369     const PetscInt    *perm  = perms ? perms[p] : NULL;
6370     const PetscScalar *flip  = flips ? flips[p] : NULL;
6371     PetscInt           dof, off, d;
6372     const PetscScalar *varr;
6373 
6374     PetscCall(PetscSectionGetDof(section, point, &dof));
6375     PetscCall(PetscSectionGetOffset(section, point, &off));
6376     varr = PetscSafePointerPlusOffset(vArray, off);
6377     if (clperm) {
6378       if (perm) {
6379         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
6380       } else {
6381         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
6382       }
6383       if (flip) {
6384         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
6385       }
6386     } else {
6387       if (perm) {
6388         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
6389       } else {
6390         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
6391       }
6392       if (flip) {
6393         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
6394       }
6395     }
6396     offset += dof;
6397   }
6398   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6399   *size = offset;
6400   PetscFunctionReturn(PETSC_SUCCESS);
6401 }
6402 
6403 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[])
6404 {
6405   PetscInt offset = 0, f;
6406 
6407   PetscFunctionBeginHot;
6408   *size = 0;
6409   for (f = 0; f < numFields; ++f) {
6410     PetscInt            p;
6411     const PetscInt    **perms = NULL;
6412     const PetscScalar **flips = NULL;
6413 
6414     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6415     for (p = 0; p < numPoints; p++) {
6416       const PetscInt     point = points[2 * p];
6417       PetscInt           fdof, foff, b;
6418       const PetscScalar *varr;
6419       const PetscInt    *perm = perms ? perms[p] : NULL;
6420       const PetscScalar *flip = flips ? flips[p] : NULL;
6421 
6422       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6423       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6424       varr = &vArray[foff];
6425       if (clperm) {
6426         if (perm) {
6427           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
6428         } else {
6429           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
6430         }
6431         if (flip) {
6432           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
6433         }
6434       } else {
6435         if (perm) {
6436           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
6437         } else {
6438           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
6439         }
6440         if (flip) {
6441           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
6442         }
6443       }
6444       offset += fdof;
6445     }
6446     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6447   }
6448   *size = offset;
6449   PetscFunctionReturn(PETSC_SUCCESS);
6450 }
6451 
6452 PetscErrorCode DMPlexVecGetOrientedClosure_Internal(DM dm, PetscSection section, PetscBool useClPerm, Vec v, PetscInt point, PetscInt ornt, PetscInt *csize, PetscScalar *values[])
6453 {
6454   PetscSection    clSection;
6455   IS              clPoints;
6456   PetscInt       *points = NULL;
6457   const PetscInt *clp, *perm = NULL;
6458   PetscInt        depth, numFields, numPoints, asize;
6459 
6460   PetscFunctionBeginHot;
6461   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6462   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6463   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6464   PetscValidHeaderSpecific(v, VEC_CLASSID, 4);
6465   PetscCall(DMPlexGetDepth(dm, &depth));
6466   PetscCall(PetscSectionGetNumFields(section, &numFields));
6467   if (depth == 1 && numFields < 2) {
6468     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6469     PetscFunctionReturn(PETSC_SUCCESS);
6470   }
6471   /* Get points */
6472   PetscCall(DMPlexGetCompressedClosure(dm, section, point, ornt, &numPoints, &points, &clSection, &clPoints, &clp));
6473   /* Get sizes */
6474   asize = 0;
6475   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6476     PetscInt dof;
6477     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6478     asize += dof;
6479   }
6480   if (values) {
6481     const PetscScalar *vArray;
6482     PetscInt           size;
6483 
6484     if (*values) {
6485       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);
6486     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6487     if (useClPerm) PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6488     PetscCall(VecGetArrayRead(v, &vArray));
6489     /* Get values */
6490     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6491     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6492     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6493     /* Cleanup array */
6494     PetscCall(VecRestoreArrayRead(v, &vArray));
6495   }
6496   if (csize) *csize = asize;
6497   /* Cleanup points */
6498   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6499   PetscFunctionReturn(PETSC_SUCCESS);
6500 }
6501 
6502 /*@C
6503   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
6504 
6505   Not collective
6506 
6507   Input Parameters:
6508 + dm      - The `DM`
6509 . section - The section describing the layout in `v`, or `NULL` to use the default section
6510 . v       - The local vector
6511 - point   - The point in the `DM`
6512 
6513   Input/Output Parameters:
6514 + csize  - The size of the input values array, or `NULL`; on output the number of values in the closure
6515 - values - An array to use for the values, or *values = `NULL` to have it allocated automatically;
6516            if the user provided `NULL`, it is a borrowed array and should not be freed, use  `DMPlexVecRestoreClosure()` to return it
6517 
6518   Level: intermediate
6519 
6520   Notes:
6521   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6522   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
6523   assembly function, and a user may already have allocated storage for this operation.
6524 
6525   A typical use could be
6526 .vb
6527    values = NULL;
6528    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6529    for (cl = 0; cl < clSize; ++cl) {
6530      <Compute on closure>
6531    }
6532    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
6533 .ve
6534   or
6535 .vb
6536    PetscMalloc1(clMaxSize, &values);
6537    for (p = pStart; p < pEnd; ++p) {
6538      clSize = clMaxSize;
6539      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6540      for (cl = 0; cl < clSize; ++cl) {
6541        <Compute on closure>
6542      }
6543    }
6544    PetscFree(values);
6545 .ve
6546 
6547   Fortran Notes:
6548   The `csize` argument is not present in the Fortran binding.
6549 
6550   `values` must be declared with
6551 .vb
6552   PetscScalar,dimension(:),pointer   :: values
6553 .ve
6554   and it will be allocated internally by PETSc to hold the values returned
6555 
6556 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6557 @*/
6558 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6559 {
6560   PetscFunctionBeginHot;
6561   PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, section, PETSC_TRUE, v, point, 0, csize, values));
6562   PetscFunctionReturn(PETSC_SUCCESS);
6563 }
6564 
6565 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6566 {
6567   DMLabel            depthLabel;
6568   PetscSection       clSection;
6569   IS                 clPoints;
6570   PetscScalar       *array;
6571   const PetscScalar *vArray;
6572   PetscInt          *points = NULL;
6573   const PetscInt    *clp, *perm = NULL;
6574   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6575 
6576   PetscFunctionBeginHot;
6577   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6578   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6579   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6580   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6581   PetscCall(DMPlexGetDepth(dm, &mdepth));
6582   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6583   PetscCall(PetscSectionGetNumFields(section, &numFields));
6584   if (mdepth == 1 && numFields < 2) {
6585     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6586     PetscFunctionReturn(PETSC_SUCCESS);
6587   }
6588   /* Get points */
6589   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6590   for (clsize = 0, p = 0; p < Np; p++) {
6591     PetscInt dof;
6592     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6593     clsize += dof;
6594   }
6595   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6596   /* Filter points */
6597   for (p = 0; p < numPoints * 2; p += 2) {
6598     PetscInt dep;
6599 
6600     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6601     if (dep != depth) continue;
6602     points[Np * 2 + 0] = points[p];
6603     points[Np * 2 + 1] = points[p + 1];
6604     ++Np;
6605   }
6606   /* Get array */
6607   if (!values || !*values) {
6608     PetscInt asize = 0, dof;
6609 
6610     for (p = 0; p < Np * 2; p += 2) {
6611       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6612       asize += dof;
6613     }
6614     if (!values) {
6615       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6616       if (csize) *csize = asize;
6617       PetscFunctionReturn(PETSC_SUCCESS);
6618     }
6619     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6620   } else {
6621     array = *values;
6622   }
6623   PetscCall(VecGetArrayRead(v, &vArray));
6624   /* Get values */
6625   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6626   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6627   /* Cleanup points */
6628   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6629   /* Cleanup array */
6630   PetscCall(VecRestoreArrayRead(v, &vArray));
6631   if (!*values) {
6632     if (csize) *csize = size;
6633     *values = array;
6634   } else {
6635     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6636     *csize = size;
6637   }
6638   PetscFunctionReturn(PETSC_SUCCESS);
6639 }
6640 
6641 /*@C
6642   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point' obtained with `DMPlexVecGetClosure()`
6643 
6644   Not collective
6645 
6646   Input Parameters:
6647 + dm      - The `DM`
6648 . section - The section describing the layout in `v`, or `NULL` to use the default section
6649 . v       - The local vector
6650 . point   - The point in the `DM`
6651 . csize   - The number of values in the closure, or `NULL`
6652 - values  - The array of values
6653 
6654   Level: intermediate
6655 
6656   Note:
6657   The array values are discarded and not copied back into `v`. In order to copy values back to `v`, use `DMPlexVecSetClosure()`
6658 
6659   Fortran Note:
6660   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6661 
6662 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6663 @*/
6664 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6665 {
6666   PetscInt size = 0;
6667 
6668   PetscFunctionBegin;
6669   /* Should work without recalculating size */
6670   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6671   *values = NULL;
6672   PetscFunctionReturn(PETSC_SUCCESS);
6673 }
6674 
6675 static inline void add(PetscScalar *x, PetscScalar y)
6676 {
6677   *x += y;
6678 }
6679 static inline void insert(PetscScalar *x, PetscScalar y)
6680 {
6681   *x = y;
6682 }
6683 
6684 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[])
6685 {
6686   PetscInt        cdof;  /* The number of constraints on this point */
6687   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6688   PetscScalar    *a;
6689   PetscInt        off, cind = 0, k;
6690 
6691   PetscFunctionBegin;
6692   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6693   PetscCall(PetscSectionGetOffset(section, point, &off));
6694   a = &array[off];
6695   if (!cdof || setBC) {
6696     if (clperm) {
6697       if (perm) {
6698         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6699       } else {
6700         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6701       }
6702     } else {
6703       if (perm) {
6704         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6705       } else {
6706         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6707       }
6708     }
6709   } else {
6710     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6711     if (clperm) {
6712       if (perm) {
6713         for (k = 0; k < dof; ++k) {
6714           if ((cind < cdof) && (k == cdofs[cind])) {
6715             ++cind;
6716             continue;
6717           }
6718           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6719         }
6720       } else {
6721         for (k = 0; k < dof; ++k) {
6722           if ((cind < cdof) && (k == cdofs[cind])) {
6723             ++cind;
6724             continue;
6725           }
6726           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6727         }
6728       }
6729     } else {
6730       if (perm) {
6731         for (k = 0; k < dof; ++k) {
6732           if ((cind < cdof) && (k == cdofs[cind])) {
6733             ++cind;
6734             continue;
6735           }
6736           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6737         }
6738       } else {
6739         for (k = 0; k < dof; ++k) {
6740           if ((cind < cdof) && (k == cdofs[cind])) {
6741             ++cind;
6742             continue;
6743           }
6744           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6745         }
6746       }
6747     }
6748   }
6749   PetscFunctionReturn(PETSC_SUCCESS);
6750 }
6751 
6752 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[])
6753 {
6754   PetscInt        cdof;  /* The number of constraints on this point */
6755   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6756   PetscScalar    *a;
6757   PetscInt        off, cind = 0, k;
6758 
6759   PetscFunctionBegin;
6760   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6761   PetscCall(PetscSectionGetOffset(section, point, &off));
6762   a = &array[off];
6763   if (cdof) {
6764     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6765     if (clperm) {
6766       if (perm) {
6767         for (k = 0; k < dof; ++k) {
6768           if ((cind < cdof) && (k == cdofs[cind])) {
6769             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6770             cind++;
6771           }
6772         }
6773       } else {
6774         for (k = 0; k < dof; ++k) {
6775           if ((cind < cdof) && (k == cdofs[cind])) {
6776             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6777             cind++;
6778           }
6779         }
6780       }
6781     } else {
6782       if (perm) {
6783         for (k = 0; k < dof; ++k) {
6784           if ((cind < cdof) && (k == cdofs[cind])) {
6785             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6786             cind++;
6787           }
6788         }
6789       } else {
6790         for (k = 0; k < dof; ++k) {
6791           if ((cind < cdof) && (k == cdofs[cind])) {
6792             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6793             cind++;
6794           }
6795         }
6796       }
6797     }
6798   }
6799   PetscFunctionReturn(PETSC_SUCCESS);
6800 }
6801 
6802 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[])
6803 {
6804   PetscScalar    *a;
6805   PetscInt        fdof, foff, fcdof, foffset = *offset;
6806   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6807   PetscInt        cind = 0, b;
6808 
6809   PetscFunctionBegin;
6810   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6811   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6812   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6813   a = &array[foff];
6814   if (!fcdof || setBC) {
6815     if (clperm) {
6816       if (perm) {
6817         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6818       } else {
6819         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6820       }
6821     } else {
6822       if (perm) {
6823         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6824       } else {
6825         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6826       }
6827     }
6828   } else {
6829     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6830     if (clperm) {
6831       if (perm) {
6832         for (b = 0; b < fdof; b++) {
6833           if ((cind < fcdof) && (b == fcdofs[cind])) {
6834             ++cind;
6835             continue;
6836           }
6837           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6838         }
6839       } else {
6840         for (b = 0; b < fdof; b++) {
6841           if ((cind < fcdof) && (b == fcdofs[cind])) {
6842             ++cind;
6843             continue;
6844           }
6845           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6846         }
6847       }
6848     } else {
6849       if (perm) {
6850         for (b = 0; b < fdof; b++) {
6851           if ((cind < fcdof) && (b == fcdofs[cind])) {
6852             ++cind;
6853             continue;
6854           }
6855           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6856         }
6857       } else {
6858         for (b = 0; b < fdof; b++) {
6859           if ((cind < fcdof) && (b == fcdofs[cind])) {
6860             ++cind;
6861             continue;
6862           }
6863           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6864         }
6865       }
6866     }
6867   }
6868   *offset += fdof;
6869   PetscFunctionReturn(PETSC_SUCCESS);
6870 }
6871 
6872 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[])
6873 {
6874   PetscScalar    *a;
6875   PetscInt        fdof, foff, fcdof, foffset = *offset;
6876   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6877   PetscInt        Nc, cind = 0, ncind = 0, b;
6878   PetscBool       ncSet, fcSet;
6879 
6880   PetscFunctionBegin;
6881   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6882   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6883   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6884   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6885   a = &array[foff];
6886   if (fcdof) {
6887     /* We just override fcdof and fcdofs with Ncc and comps */
6888     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6889     if (clperm) {
6890       if (perm) {
6891         if (comps) {
6892           for (b = 0; b < fdof; b++) {
6893             ncSet = fcSet = PETSC_FALSE;
6894             if (b % Nc == comps[ncind]) {
6895               ncind = (ncind + 1) % Ncc;
6896               ncSet = PETSC_TRUE;
6897             }
6898             if ((cind < fcdof) && (b == fcdofs[cind])) {
6899               ++cind;
6900               fcSet = PETSC_TRUE;
6901             }
6902             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6903           }
6904         } else {
6905           for (b = 0; b < fdof; b++) {
6906             if ((cind < fcdof) && (b == fcdofs[cind])) {
6907               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6908               ++cind;
6909             }
6910           }
6911         }
6912       } else {
6913         if (comps) {
6914           for (b = 0; b < fdof; b++) {
6915             ncSet = fcSet = PETSC_FALSE;
6916             if (b % Nc == comps[ncind]) {
6917               ncind = (ncind + 1) % Ncc;
6918               ncSet = PETSC_TRUE;
6919             }
6920             if ((cind < fcdof) && (b == fcdofs[cind])) {
6921               ++cind;
6922               fcSet = PETSC_TRUE;
6923             }
6924             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6925           }
6926         } else {
6927           for (b = 0; b < fdof; b++) {
6928             if ((cind < fcdof) && (b == fcdofs[cind])) {
6929               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6930               ++cind;
6931             }
6932           }
6933         }
6934       }
6935     } else {
6936       if (perm) {
6937         if (comps) {
6938           for (b = 0; b < fdof; b++) {
6939             ncSet = fcSet = PETSC_FALSE;
6940             if (b % Nc == comps[ncind]) {
6941               ncind = (ncind + 1) % Ncc;
6942               ncSet = PETSC_TRUE;
6943             }
6944             if ((cind < fcdof) && (b == fcdofs[cind])) {
6945               ++cind;
6946               fcSet = PETSC_TRUE;
6947             }
6948             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6949           }
6950         } else {
6951           for (b = 0; b < fdof; b++) {
6952             if ((cind < fcdof) && (b == fcdofs[cind])) {
6953               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6954               ++cind;
6955             }
6956           }
6957         }
6958       } else {
6959         if (comps) {
6960           for (b = 0; b < fdof; b++) {
6961             ncSet = fcSet = PETSC_FALSE;
6962             if (b % Nc == comps[ncind]) {
6963               ncind = (ncind + 1) % Ncc;
6964               ncSet = PETSC_TRUE;
6965             }
6966             if ((cind < fcdof) && (b == fcdofs[cind])) {
6967               ++cind;
6968               fcSet = PETSC_TRUE;
6969             }
6970             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6971           }
6972         } else {
6973           for (b = 0; b < fdof; b++) {
6974             if ((cind < fcdof) && (b == fcdofs[cind])) {
6975               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6976               ++cind;
6977             }
6978           }
6979         }
6980       }
6981     }
6982   }
6983   *offset += fdof;
6984   PetscFunctionReturn(PETSC_SUCCESS);
6985 }
6986 
6987 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6988 {
6989   PetscScalar    *array;
6990   const PetscInt *cone, *coneO;
6991   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6992 
6993   PetscFunctionBeginHot;
6994   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6995   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6996   PetscCall(DMPlexGetCone(dm, point, &cone));
6997   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6998   PetscCall(VecGetArray(v, &array));
6999   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
7000     const PetscInt cp = !p ? point : cone[p - 1];
7001     const PetscInt o  = !p ? 0 : coneO[p - 1];
7002 
7003     if ((cp < pStart) || (cp >= pEnd)) {
7004       dof = 0;
7005       continue;
7006     }
7007     PetscCall(PetscSectionGetDof(section, cp, &dof));
7008     /* ADD_VALUES */
7009     {
7010       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7011       PetscScalar    *a;
7012       PetscInt        cdof, coff, cind = 0, k;
7013 
7014       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
7015       PetscCall(PetscSectionGetOffset(section, cp, &coff));
7016       a = &array[coff];
7017       if (!cdof) {
7018         if (o >= 0) {
7019           for (k = 0; k < dof; ++k) a[k] += values[off + k];
7020         } else {
7021           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
7022         }
7023       } else {
7024         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
7025         if (o >= 0) {
7026           for (k = 0; k < dof; ++k) {
7027             if ((cind < cdof) && (k == cdofs[cind])) {
7028               ++cind;
7029               continue;
7030             }
7031             a[k] += values[off + k];
7032           }
7033         } else {
7034           for (k = 0; k < dof; ++k) {
7035             if ((cind < cdof) && (k == cdofs[cind])) {
7036               ++cind;
7037               continue;
7038             }
7039             a[k] += values[off + dof - k - 1];
7040           }
7041         }
7042       }
7043     }
7044   }
7045   PetscCall(VecRestoreArray(v, &array));
7046   PetscFunctionReturn(PETSC_SUCCESS);
7047 }
7048 
7049 /*@C
7050   DMPlexVecSetClosure - Set an array of the values on the closure of `point`
7051 
7052   Not collective
7053 
7054   Input Parameters:
7055 + dm      - The `DM`
7056 . section - The section describing the layout in `v`, or `NULL` to use the default section
7057 . v       - The local vector
7058 . point   - The point in the `DM`
7059 . values  - The array of values
7060 - mode    - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
7061             where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
7062 
7063   Level: intermediate
7064 
7065   Note:
7066   Usually the input arrays were obtained with `DMPlexVecGetClosure()`
7067 
7068   Fortran Note:
7069   `values` must be declared with
7070 .vb
7071   PetscScalar,dimension(:),pointer   :: values
7072 .ve
7073 
7074 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
7075 @*/
7076 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
7077 {
7078   PetscSection    clSection;
7079   IS              clPoints;
7080   PetscScalar    *array;
7081   PetscInt       *points = NULL;
7082   const PetscInt *clp, *clperm = NULL;
7083   PetscInt        depth, numFields, numPoints, p, clsize;
7084 
7085   PetscFunctionBeginHot;
7086   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7087   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7088   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7089   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7090   PetscCall(DMPlexGetDepth(dm, &depth));
7091   PetscCall(PetscSectionGetNumFields(section, &numFields));
7092   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
7093     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
7094     PetscFunctionReturn(PETSC_SUCCESS);
7095   }
7096   /* Get points */
7097   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7098   for (clsize = 0, p = 0; p < numPoints; p++) {
7099     PetscInt dof;
7100     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7101     clsize += dof;
7102   }
7103   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7104   /* Get array */
7105   PetscCall(VecGetArray(v, &array));
7106   /* Get values */
7107   if (numFields > 0) {
7108     PetscInt offset = 0, f;
7109     for (f = 0; f < numFields; ++f) {
7110       const PetscInt    **perms = NULL;
7111       const PetscScalar **flips = NULL;
7112 
7113       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7114       switch (mode) {
7115       case INSERT_VALUES:
7116         for (p = 0; p < numPoints; p++) {
7117           const PetscInt     point = points[2 * p];
7118           const PetscInt    *perm  = perms ? perms[p] : NULL;
7119           const PetscScalar *flip  = flips ? flips[p] : NULL;
7120           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array));
7121         }
7122         break;
7123       case INSERT_ALL_VALUES:
7124         for (p = 0; p < numPoints; p++) {
7125           const PetscInt     point = points[2 * p];
7126           const PetscInt    *perm  = perms ? perms[p] : NULL;
7127           const PetscScalar *flip  = flips ? flips[p] : NULL;
7128           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array));
7129         }
7130         break;
7131       case INSERT_BC_VALUES:
7132         for (p = 0; p < numPoints; p++) {
7133           const PetscInt     point = points[2 * p];
7134           const PetscInt    *perm  = perms ? perms[p] : NULL;
7135           const PetscScalar *flip  = flips ? flips[p] : NULL;
7136           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array));
7137         }
7138         break;
7139       case ADD_VALUES:
7140         for (p = 0; p < numPoints; p++) {
7141           const PetscInt     point = points[2 * p];
7142           const PetscInt    *perm  = perms ? perms[p] : NULL;
7143           const PetscScalar *flip  = flips ? flips[p] : NULL;
7144           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array));
7145         }
7146         break;
7147       case ADD_ALL_VALUES:
7148         for (p = 0; p < numPoints; p++) {
7149           const PetscInt     point = points[2 * p];
7150           const PetscInt    *perm  = perms ? perms[p] : NULL;
7151           const PetscScalar *flip  = flips ? flips[p] : NULL;
7152           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array));
7153         }
7154         break;
7155       case ADD_BC_VALUES:
7156         for (p = 0; p < numPoints; p++) {
7157           const PetscInt     point = points[2 * p];
7158           const PetscInt    *perm  = perms ? perms[p] : NULL;
7159           const PetscScalar *flip  = flips ? flips[p] : NULL;
7160           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array));
7161         }
7162         break;
7163       default:
7164         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7165       }
7166       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7167     }
7168   } else {
7169     PetscInt            dof, off;
7170     const PetscInt    **perms = NULL;
7171     const PetscScalar **flips = NULL;
7172 
7173     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
7174     switch (mode) {
7175     case INSERT_VALUES:
7176       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7177         const PetscInt     point = points[2 * p];
7178         const PetscInt    *perm  = perms ? perms[p] : NULL;
7179         const PetscScalar *flip  = flips ? flips[p] : NULL;
7180         PetscCall(PetscSectionGetDof(section, point, &dof));
7181         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array));
7182       }
7183       break;
7184     case INSERT_ALL_VALUES:
7185       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7186         const PetscInt     point = points[2 * p];
7187         const PetscInt    *perm  = perms ? perms[p] : NULL;
7188         const PetscScalar *flip  = flips ? flips[p] : NULL;
7189         PetscCall(PetscSectionGetDof(section, point, &dof));
7190         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array));
7191       }
7192       break;
7193     case INSERT_BC_VALUES:
7194       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7195         const PetscInt     point = points[2 * p];
7196         const PetscInt    *perm  = perms ? perms[p] : NULL;
7197         const PetscScalar *flip  = flips ? flips[p] : NULL;
7198         PetscCall(PetscSectionGetDof(section, point, &dof));
7199         PetscCall(updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array));
7200       }
7201       break;
7202     case ADD_VALUES:
7203       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7204         const PetscInt     point = points[2 * p];
7205         const PetscInt    *perm  = perms ? perms[p] : NULL;
7206         const PetscScalar *flip  = flips ? flips[p] : NULL;
7207         PetscCall(PetscSectionGetDof(section, point, &dof));
7208         PetscCall(updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array));
7209       }
7210       break;
7211     case ADD_ALL_VALUES:
7212       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7213         const PetscInt     point = points[2 * p];
7214         const PetscInt    *perm  = perms ? perms[p] : NULL;
7215         const PetscScalar *flip  = flips ? flips[p] : NULL;
7216         PetscCall(PetscSectionGetDof(section, point, &dof));
7217         PetscCall(updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array));
7218       }
7219       break;
7220     case ADD_BC_VALUES:
7221       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7222         const PetscInt     point = points[2 * p];
7223         const PetscInt    *perm  = perms ? perms[p] : NULL;
7224         const PetscScalar *flip  = flips ? flips[p] : NULL;
7225         PetscCall(PetscSectionGetDof(section, point, &dof));
7226         PetscCall(updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array));
7227       }
7228       break;
7229     default:
7230       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7231     }
7232     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
7233   }
7234   /* Cleanup points */
7235   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7236   /* Cleanup array */
7237   PetscCall(VecRestoreArray(v, &array));
7238   PetscFunctionReturn(PETSC_SUCCESS);
7239 }
7240 
7241 /* Check whether the given point is in the label. If not, update the offset to skip this point */
7242 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
7243 {
7244   PetscFunctionBegin;
7245   *contains = PETSC_TRUE;
7246   if (label) {
7247     PetscInt fdof;
7248 
7249     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
7250     if (!*contains) {
7251       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7252       *offset += fdof;
7253       PetscFunctionReturn(PETSC_SUCCESS);
7254     }
7255   }
7256   PetscFunctionReturn(PETSC_SUCCESS);
7257 }
7258 
7259 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
7260 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)
7261 {
7262   PetscSection    clSection;
7263   IS              clPoints;
7264   PetscScalar    *array;
7265   PetscInt       *points = NULL;
7266   const PetscInt *clp;
7267   PetscInt        numFields, numPoints, p;
7268   PetscInt        offset = 0, f;
7269 
7270   PetscFunctionBeginHot;
7271   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7272   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7273   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7274   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7275   PetscCall(PetscSectionGetNumFields(section, &numFields));
7276   /* Get points */
7277   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7278   /* Get array */
7279   PetscCall(VecGetArray(v, &array));
7280   /* Get values */
7281   for (f = 0; f < numFields; ++f) {
7282     const PetscInt    **perms = NULL;
7283     const PetscScalar **flips = NULL;
7284     PetscBool           contains;
7285 
7286     if (!fieldActive[f]) {
7287       for (p = 0; p < numPoints * 2; p += 2) {
7288         PetscInt fdof;
7289         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7290         offset += fdof;
7291       }
7292       continue;
7293     }
7294     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7295     switch (mode) {
7296     case INSERT_VALUES:
7297       for (p = 0; p < numPoints; p++) {
7298         const PetscInt     point = points[2 * p];
7299         const PetscInt    *perm  = perms ? perms[p] : NULL;
7300         const PetscScalar *flip  = flips ? flips[p] : NULL;
7301         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7302         if (!contains) continue;
7303         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
7304       }
7305       break;
7306     case INSERT_ALL_VALUES:
7307       for (p = 0; p < numPoints; p++) {
7308         const PetscInt     point = points[2 * p];
7309         const PetscInt    *perm  = perms ? perms[p] : NULL;
7310         const PetscScalar *flip  = flips ? flips[p] : NULL;
7311         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7312         if (!contains) continue;
7313         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
7314       }
7315       break;
7316     case INSERT_BC_VALUES:
7317       for (p = 0; p < numPoints; p++) {
7318         const PetscInt     point = points[2 * p];
7319         const PetscInt    *perm  = perms ? perms[p] : NULL;
7320         const PetscScalar *flip  = flips ? flips[p] : NULL;
7321         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7322         if (!contains) continue;
7323         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
7324       }
7325       break;
7326     case ADD_VALUES:
7327       for (p = 0; p < numPoints; p++) {
7328         const PetscInt     point = points[2 * p];
7329         const PetscInt    *perm  = perms ? perms[p] : NULL;
7330         const PetscScalar *flip  = flips ? flips[p] : NULL;
7331         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7332         if (!contains) continue;
7333         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
7334       }
7335       break;
7336     case ADD_ALL_VALUES:
7337       for (p = 0; p < numPoints; p++) {
7338         const PetscInt     point = points[2 * p];
7339         const PetscInt    *perm  = perms ? perms[p] : NULL;
7340         const PetscScalar *flip  = flips ? flips[p] : NULL;
7341         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7342         if (!contains) continue;
7343         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
7344       }
7345       break;
7346     default:
7347       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7348     }
7349     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7350   }
7351   /* Cleanup points */
7352   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7353   /* Cleanup array */
7354   PetscCall(VecRestoreArray(v, &array));
7355   PetscFunctionReturn(PETSC_SUCCESS);
7356 }
7357 
7358 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
7359 {
7360   PetscMPIInt rank;
7361   PetscInt    i, j;
7362 
7363   PetscFunctionBegin;
7364   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7365   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
7366   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
7367   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
7368   numCIndices = numCIndices ? numCIndices : numRIndices;
7369   if (!values) PetscFunctionReturn(PETSC_SUCCESS);
7370   for (i = 0; i < numRIndices; i++) {
7371     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
7372     for (j = 0; j < numCIndices; j++) {
7373 #if defined(PETSC_USE_COMPLEX)
7374       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
7375 #else
7376       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
7377 #endif
7378     }
7379     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
7380   }
7381   PetscFunctionReturn(PETSC_SUCCESS);
7382 }
7383 
7384 /*
7385   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
7386 
7387   Input Parameters:
7388 + section - The section for this data layout
7389 . islocal - Is the section (and thus indices being requested) local or global?
7390 . point   - The point contributing dofs with these indices
7391 . off     - The global offset of this point
7392 . loff    - The local offset of each field
7393 . setBC   - The flag determining whether to include indices of boundary values
7394 . perm    - A permutation of the dofs on this point, or NULL
7395 - indperm - A permutation of the entire indices array, or NULL
7396 
7397   Output Parameter:
7398 . indices - Indices for dofs on this point
7399 
7400   Level: developer
7401 
7402   Note: The indices could be local or global, depending on the value of 'off'.
7403 */
7404 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
7405 {
7406   PetscInt        dof;   /* The number of unknowns on this point */
7407   PetscInt        cdof;  /* The number of constraints on this point */
7408   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7409   PetscInt        cind = 0, k;
7410 
7411   PetscFunctionBegin;
7412   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7413   PetscCall(PetscSectionGetDof(section, point, &dof));
7414   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
7415   if (!cdof || setBC) {
7416     for (k = 0; k < dof; ++k) {
7417       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7418       const PetscInt ind    = indperm ? indperm[preind] : preind;
7419 
7420       indices[ind] = off + k;
7421     }
7422   } else {
7423     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
7424     for (k = 0; k < dof; ++k) {
7425       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7426       const PetscInt ind    = indperm ? indperm[preind] : preind;
7427 
7428       if ((cind < cdof) && (k == cdofs[cind])) {
7429         /* Insert check for returning constrained indices */
7430         indices[ind] = -(off + k + 1);
7431         ++cind;
7432       } else {
7433         indices[ind] = off + k - (islocal ? 0 : cind);
7434       }
7435     }
7436   }
7437   *loff += dof;
7438   PetscFunctionReturn(PETSC_SUCCESS);
7439 }
7440 
7441 /*
7442  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
7443 
7444  Input Parameters:
7445 + section - a section (global or local)
7446 - islocal - `PETSC_TRUE` if requesting local indices (i.e., section is local); `PETSC_FALSE` for global
7447 . point - point within section
7448 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
7449 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
7450 . setBC - identify constrained (boundary condition) points via involution.
7451 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
7452 . permsoff - offset
7453 - indperm - index permutation
7454 
7455  Output Parameter:
7456 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
7457 . indices - array to hold indices (as defined by section) of each dof associated with point
7458 
7459  Notes:
7460  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
7461  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
7462  in the local vector.
7463 
7464  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
7465  significant).  It is invalid to call with a global section and setBC=true.
7466 
7467  Developer Note:
7468  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
7469  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
7470  offset could be obtained from the section instead of passing it explicitly as we do now.
7471 
7472  Example:
7473  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
7474  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
7475  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
7476  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.
7477 
7478  Level: developer
7479 */
7480 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[])
7481 {
7482   PetscInt numFields, foff, f;
7483 
7484   PetscFunctionBegin;
7485   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7486   PetscCall(PetscSectionGetNumFields(section, &numFields));
7487   for (f = 0, foff = 0; f < numFields; ++f) {
7488     PetscInt        fdof, cfdof;
7489     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7490     PetscInt        cind = 0, b;
7491     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7492 
7493     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7494     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7495     if (!cfdof || setBC) {
7496       for (b = 0; b < fdof; ++b) {
7497         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7498         const PetscInt ind    = indperm ? indperm[preind] : preind;
7499 
7500         indices[ind] = off + foff + b;
7501       }
7502     } else {
7503       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7504       for (b = 0; b < fdof; ++b) {
7505         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7506         const PetscInt ind    = indperm ? indperm[preind] : preind;
7507 
7508         if ((cind < cfdof) && (b == fcdofs[cind])) {
7509           indices[ind] = -(off + foff + b + 1);
7510           ++cind;
7511         } else {
7512           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7513         }
7514       }
7515     }
7516     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7517     foffs[f] += fdof;
7518   }
7519   PetscFunctionReturn(PETSC_SUCCESS);
7520 }
7521 
7522 /*
7523   This version believes the globalSection offsets for each field, rather than just the point offset
7524 
7525  . foffs - The offset into 'indices' for each field, since it is segregated by field
7526 
7527  Notes:
7528  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7529  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7530 */
7531 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7532 {
7533   PetscInt numFields, foff, f;
7534 
7535   PetscFunctionBegin;
7536   PetscCall(PetscSectionGetNumFields(section, &numFields));
7537   for (f = 0; f < numFields; ++f) {
7538     PetscInt        fdof, cfdof;
7539     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7540     PetscInt        cind = 0, b;
7541     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7542 
7543     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7544     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7545     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7546     if (!cfdof) {
7547       for (b = 0; b < fdof; ++b) {
7548         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7549         const PetscInt ind    = indperm ? indperm[preind] : preind;
7550 
7551         indices[ind] = foff + b;
7552       }
7553     } else {
7554       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7555       for (b = 0; b < fdof; ++b) {
7556         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7557         const PetscInt ind    = indperm ? indperm[preind] : preind;
7558 
7559         if ((cind < cfdof) && (b == fcdofs[cind])) {
7560           indices[ind] = -(foff + b + 1);
7561           ++cind;
7562         } else {
7563           indices[ind] = foff + b - cind;
7564         }
7565       }
7566     }
7567     foffs[f] += fdof;
7568   }
7569   PetscFunctionReturn(PETSC_SUCCESS);
7570 }
7571 
7572 static PetscErrorCode DMPlexAnchorsGetSubMatIndices(PetscInt nPoints, const PetscInt pnts[], PetscSection section, PetscSection cSec, PetscInt tmpIndices[], PetscInt fieldOffsets[], PetscInt indices[], const PetscInt ***perms)
7573 {
7574   PetscInt numFields, sStart, sEnd, cStart, cEnd;
7575 
7576   PetscFunctionBegin;
7577   PetscCall(PetscSectionGetNumFields(section, &numFields));
7578   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7579   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7580   for (PetscInt p = 0; p < nPoints; p++) {
7581     PetscInt     b       = pnts[2 * p];
7582     PetscInt     bSecDof = 0, bOff;
7583     PetscInt     cSecDof = 0;
7584     PetscSection indices_section;
7585 
7586     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7587     if (!bSecDof) continue;
7588     if (b >= cStart && b < cEnd) PetscCall(PetscSectionGetDof(cSec, b, &cSecDof));
7589     indices_section = cSecDof > 0 ? cSec : section;
7590     if (numFields) {
7591       PetscInt fStart[32], fEnd[32];
7592 
7593       fStart[0] = 0;
7594       fEnd[0]   = 0;
7595       for (PetscInt f = 0; f < numFields; f++) {
7596         PetscInt fDof = 0;
7597 
7598         PetscCall(PetscSectionGetFieldDof(indices_section, b, f, &fDof));
7599         fStart[f + 1] = fStart[f] + fDof;
7600         fEnd[f + 1]   = fStart[f + 1];
7601       }
7602       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7603       // only apply permutations on one side
7604       PetscCall(DMPlexGetIndicesPointFields_Internal(indices_section, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, perms ? p : -1, NULL, tmpIndices));
7605       for (PetscInt f = 0; f < numFields; f++) {
7606         for (PetscInt i = fStart[f]; i < fEnd[f]; i++) { indices[fieldOffsets[f]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1); }
7607       }
7608     } else {
7609       PetscInt bEnd = 0;
7610 
7611       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7612       PetscCall(DMPlexGetIndicesPoint_Internal(indices_section, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, tmpIndices));
7613 
7614       for (PetscInt i = 0; i < bEnd; i++) indices[fieldOffsets[0]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1);
7615     }
7616   }
7617   PetscFunctionReturn(PETSC_SUCCESS);
7618 }
7619 
7620 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[])
7621 {
7622   Mat             cMat;
7623   PetscSection    aSec, cSec;
7624   IS              aIS;
7625   PetscInt        aStart = -1, aEnd = -1;
7626   PetscInt        sStart = -1, sEnd = -1;
7627   PetscInt        cStart = -1, cEnd = -1;
7628   const PetscInt *anchors;
7629   PetscInt        numFields, p;
7630   PetscInt        newNumPoints = 0, newNumIndices = 0;
7631   PetscInt       *newPoints, *indices, *newIndices, *tmpIndices, *tmpNewIndices;
7632   PetscInt        oldOffsets[32];
7633   PetscInt        newOffsets[32];
7634   PetscInt        oldOffsetsCopy[32];
7635   PetscInt        newOffsetsCopy[32];
7636   PetscScalar    *modMat         = NULL;
7637   PetscBool       anyConstrained = PETSC_FALSE;
7638 
7639   PetscFunctionBegin;
7640   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7641   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7642   PetscCall(PetscSectionGetNumFields(section, &numFields));
7643 
7644   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7645   /* if there are point-to-point constraints */
7646   if (aSec) {
7647     PetscCall(PetscArrayzero(newOffsets, 32));
7648     PetscCall(PetscArrayzero(oldOffsets, 32));
7649     PetscCall(ISGetIndices(aIS, &anchors));
7650     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7651     PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7652     /* figure out how many points are going to be in the new element matrix
7653      * (we allow double counting, because it's all just going to be summed
7654      * into the global matrix anyway) */
7655     for (p = 0; p < 2 * numPoints; p += 2) {
7656       PetscInt b    = points[p];
7657       PetscInt bDof = 0, bSecDof = 0;
7658 
7659       if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7660       if (!bSecDof) continue;
7661 
7662       for (PetscInt f = 0; f < numFields; f++) {
7663         PetscInt fDof = 0;
7664 
7665         PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7666         oldOffsets[f + 1] += fDof;
7667       }
7668       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7669       if (bDof) {
7670         /* this point is constrained */
7671         /* it is going to be replaced by its anchors */
7672         PetscInt bOff, q;
7673 
7674         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7675         for (q = 0; q < bDof; q++) {
7676           PetscInt a    = anchors[bOff + q];
7677           PetscInt aDof = 0;
7678 
7679           if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7680           if (aDof) {
7681             anyConstrained = PETSC_TRUE;
7682             newNumPoints += 1;
7683           }
7684           newNumIndices += aDof;
7685           for (PetscInt f = 0; f < numFields; ++f) {
7686             PetscInt fDof = 0;
7687 
7688             if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7689             newOffsets[f + 1] += fDof;
7690           }
7691         }
7692       } else {
7693         /* this point is not constrained */
7694         newNumPoints++;
7695         newNumIndices += bSecDof;
7696         for (PetscInt f = 0; f < numFields; ++f) {
7697           PetscInt fDof;
7698 
7699           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7700           newOffsets[f + 1] += fDof;
7701         }
7702       }
7703     }
7704   }
7705   if (!anyConstrained) {
7706     if (outNumPoints) *outNumPoints = 0;
7707     if (outNumIndices) *outNumIndices = 0;
7708     if (outPoints) *outPoints = NULL;
7709     if (outMat) *outMat = NULL;
7710     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7711     PetscFunctionReturn(PETSC_SUCCESS);
7712   }
7713 
7714   if (outNumPoints) *outNumPoints = newNumPoints;
7715   if (outNumIndices) *outNumIndices = newNumIndices;
7716 
7717   for (PetscInt f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7718   for (PetscInt f = 0; f < numFields; ++f) oldOffsets[f + 1] += oldOffsets[f];
7719 
7720   if (!outPoints && !outMat) {
7721     if (offsets) {
7722       for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7723     }
7724     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7725     PetscFunctionReturn(PETSC_SUCCESS);
7726   }
7727 
7728   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7729   PetscCheck(!numFields || oldOffsets[numFields] == numIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, oldOffsets[numFields], numIndices);
7730 
7731   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7732   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7733 
7734   /* output arrays */
7735   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7736   PetscCall(PetscArrayzero(newPoints, 2 * newNumPoints));
7737 
7738   // get the new Points
7739   for (PetscInt p = 0, newP = 0; p < numPoints; p++) {
7740     PetscInt b    = points[2 * p];
7741     PetscInt bDof = 0, bSecDof = 0, bOff;
7742 
7743     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7744     if (!bSecDof) continue;
7745     if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7746     if (bDof) {
7747       PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7748       for (PetscInt q = 0; q < bDof; q++) {
7749         PetscInt a = anchors[bOff + q], aDof = 0;
7750 
7751         if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7752         if (aDof) {
7753           newPoints[2 * newP]     = a;
7754           newPoints[2 * newP + 1] = 0; // orientations are accounted for in constructing the matrix, newly added points are in default orientation
7755           newP++;
7756         }
7757       }
7758     } else {
7759       newPoints[2 * newP]     = b;
7760       newPoints[2 * newP + 1] = points[2 * p + 1];
7761       newP++;
7762     }
7763   }
7764 
7765   if (outMat) {
7766     PetscScalar *tmpMat;
7767     PetscCall(PetscArraycpy(oldOffsetsCopy, oldOffsets, 32));
7768     PetscCall(PetscArraycpy(newOffsetsCopy, newOffsets, 32));
7769 
7770     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &indices));
7771     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7772     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7773     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7774 
7775     for (PetscInt i = 0; i < numIndices; i++) indices[i] = -1;
7776     for (PetscInt i = 0; i < newNumIndices; i++) newIndices[i] = -1;
7777 
7778     PetscCall(DMPlexAnchorsGetSubMatIndices(numPoints, points, section, cSec, tmpIndices, oldOffsetsCopy, indices, perms));
7779     PetscCall(DMPlexAnchorsGetSubMatIndices(newNumPoints, newPoints, section, section, tmpNewIndices, newOffsetsCopy, newIndices, NULL));
7780 
7781     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
7782     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7783     PetscCall(PetscArrayzero(modMat, newNumIndices * numIndices));
7784     // for each field, insert the anchor modification into modMat
7785     for (PetscInt f = 0; f < PetscMax(1, numFields); f++) {
7786       PetscInt fStart    = oldOffsets[f];
7787       PetscInt fNewStart = newOffsets[f];
7788       for (PetscInt p = 0, newP = 0, o = fStart, oNew = fNewStart; p < numPoints; p++) {
7789         PetscInt b    = points[2 * p];
7790         PetscInt bDof = 0, bSecDof = 0, bOff;
7791 
7792         if (b >= sStart && b < sEnd) {
7793           if (numFields) {
7794             PetscCall(PetscSectionGetFieldDof(section, b, f, &bSecDof));
7795           } else {
7796             PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7797           }
7798         }
7799         if (!bSecDof) continue;
7800         if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7801         if (bDof) {
7802           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7803           for (PetscInt q = 0; q < bDof; q++, newP++) {
7804             PetscInt a = anchors[bOff + q], aDof = 0;
7805 
7806             if (a >= sStart && a < sEnd) {
7807               if (numFields) {
7808                 PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
7809               } else {
7810                 PetscCall(PetscSectionGetDof(section, a, &aDof));
7811               }
7812             }
7813             if (aDof) {
7814               PetscCall(MatGetValues(cMat, bSecDof, &indices[o], aDof, &newIndices[oNew], tmpMat));
7815               for (PetscInt d = 0; d < bSecDof; d++) {
7816                 for (PetscInt e = 0; e < aDof; e++) modMat[(o + d) * newNumIndices + oNew + e] = tmpMat[d * aDof + e];
7817               }
7818             }
7819             oNew += aDof;
7820           }
7821         } else {
7822           // Insert the identity matrix in this block
7823           for (PetscInt d = 0; d < bSecDof; d++) modMat[(o + d) * newNumIndices + oNew + d] = 1;
7824           oNew += bSecDof;
7825           newP++;
7826         }
7827         o += bSecDof;
7828       }
7829     }
7830 
7831     *outMat = modMat;
7832 
7833     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7834     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7835     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7836     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7837     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &indices));
7838   }
7839   PetscCall(ISRestoreIndices(aIS, &anchors));
7840 
7841   /* output */
7842   if (outPoints) {
7843     *outPoints = newPoints;
7844   } else {
7845     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7846   }
7847   for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7848   PetscFunctionReturn(PETSC_SUCCESS);
7849 }
7850 
7851 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)
7852 {
7853   PetscScalar *modMat        = NULL;
7854   PetscInt     newNumIndices = -1;
7855 
7856   PetscFunctionBegin;
7857   /* 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.
7858      modMat is that matrix C */
7859   PetscCall(DMPlexAnchorsGetSubMatModification(dm, section, numPoints, numIndices, points, perms, outNumPoints, &newNumIndices, outPoints, offsets, outValues ? &modMat : NULL));
7860   if (outNumIndices) *outNumIndices = newNumIndices;
7861   if (modMat) {
7862     const PetscScalar *newValues = values;
7863 
7864     if (multiplyRight) {
7865       PetscScalar *newNewValues = NULL;
7866       PetscBLASInt M            = newNumIndices;
7867       PetscBLASInt N            = numRows;
7868       PetscBLASInt K            = numIndices;
7869       PetscScalar  a = 1.0, b = 0.0;
7870 
7871       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);
7872 
7873       PetscCall(DMGetWorkArray(dm, numRows * newNumIndices, MPIU_SCALAR, &newNewValues));
7874       // row-major to column-major conversion, right multiplication becomes left multiplication
7875       PetscCallBLAS("BLASgemm", BLASgemm_("N", "N", &M, &N, &K, &a, modMat, &M, newValues, &K, &b, newNewValues, &M));
7876 
7877       numCols   = newNumIndices;
7878       newValues = newNewValues;
7879     }
7880 
7881     if (multiplyLeft) {
7882       PetscScalar *newNewValues = NULL;
7883       PetscBLASInt M            = numCols;
7884       PetscBLASInt N            = newNumIndices;
7885       PetscBLASInt K            = numIndices;
7886       PetscScalar  a = 1.0, b = 0.0;
7887 
7888       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);
7889 
7890       PetscCall(DMGetWorkArray(dm, newNumIndices * numCols, MPIU_SCALAR, &newNewValues));
7891       // row-major to column-major conversion, left multiplication becomes right multiplication
7892       PetscCallBLAS("BLASgemm", BLASgemm_("N", "T", &M, &N, &K, &a, newValues, &M, modMat, &N, &b, newNewValues, &M));
7893       if (newValues != values) PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &newValues));
7894       newValues = newNewValues;
7895     }
7896     *outValues = (PetscScalar *)newValues;
7897     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
7898   }
7899   PetscFunctionReturn(PETSC_SUCCESS);
7900 }
7901 
7902 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)
7903 {
7904   PetscFunctionBegin;
7905   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, numPoints, numIndices, points, perms, numIndices, numIndices, values, outNumPoints, outNumIndices, outPoints, outValues, offsets, PETSC_TRUE, multiplyLeft));
7906   PetscFunctionReturn(PETSC_SUCCESS);
7907 }
7908 
7909 static PetscErrorCode DMPlexGetClosureIndicesSize_Internal(DM dm, PetscSection section, PetscInt point, PetscInt *closureSize)
7910 {
7911   /* Closure ordering */
7912   PetscSection    clSection;
7913   IS              clPoints;
7914   const PetscInt *clp;
7915   PetscInt       *points;
7916   PetscInt        Ncl, Ni = 0;
7917 
7918   PetscFunctionBeginHot;
7919   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7920   for (PetscInt p = 0; p < Ncl * 2; p += 2) {
7921     PetscInt dof;
7922 
7923     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7924     Ni += dof;
7925   }
7926   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7927   *closureSize = Ni;
7928   PetscFunctionReturn(PETSC_SUCCESS);
7929 }
7930 
7931 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)
7932 {
7933   /* Closure ordering */
7934   PetscSection    clSection;
7935   IS              clPoints;
7936   const PetscInt *clp;
7937   PetscInt       *points;
7938   const PetscInt *clperm = NULL;
7939   /* Dof permutation and sign flips */
7940   const PetscInt    **perms[32] = {NULL};
7941   const PetscScalar **flips[32] = {NULL};
7942   PetscScalar        *valCopy   = NULL;
7943   /* Hanging node constraints */
7944   PetscInt    *pointsC = NULL;
7945   PetscScalar *valuesC = NULL;
7946   PetscInt     NclC, NiC;
7947 
7948   PetscInt *idx;
7949   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7950   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7951   PetscInt  idxStart, idxEnd;
7952   PetscInt  nRows, nCols;
7953 
7954   PetscFunctionBeginHot;
7955   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7956   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7957   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7958   PetscAssertPointer(numRows, 6);
7959   PetscAssertPointer(numCols, 7);
7960   if (indices) PetscAssertPointer(indices, 8);
7961   if (outOffsets) PetscAssertPointer(outOffsets, 9);
7962   if (values) PetscAssertPointer(values, 10);
7963   PetscCall(PetscSectionGetNumFields(section, &Nf));
7964   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7965   PetscCall(PetscArrayzero(offsets, 32));
7966   /* 1) Get points in closure */
7967   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7968   if (useClPerm) {
7969     PetscInt depth, clsize;
7970     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7971     for (clsize = 0, p = 0; p < Ncl; p++) {
7972       PetscInt dof;
7973       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7974       clsize += dof;
7975     }
7976     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7977   }
7978   /* 2) Get number of indices on these points and field offsets from section */
7979   for (p = 0; p < Ncl * 2; p += 2) {
7980     PetscInt dof, fdof;
7981 
7982     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7983     for (f = 0; f < Nf; ++f) {
7984       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7985       offsets[f + 1] += fdof;
7986     }
7987     Ni += dof;
7988   }
7989   if (*numRows == -1) *numRows = Ni;
7990   if (*numCols == -1) *numCols = Ni;
7991   nRows = *numRows;
7992   nCols = *numCols;
7993   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
7994   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7995   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7996   if (multiplyRight) PetscCheck(nCols == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " columns, got %" PetscInt_FMT, Ni, nCols);
7997   if (multiplyLeft) PetscCheck(nRows == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " rows, got %" PetscInt_FMT, Ni, nRows);
7998   for (f = 0; f < PetscMax(1, Nf); ++f) {
7999     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8000     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
8001     /* may need to apply sign changes to the element matrix */
8002     if (values && flips[f]) {
8003       PetscInt foffset = offsets[f];
8004 
8005       for (p = 0; p < Ncl; ++p) {
8006         PetscInt           pnt  = points[2 * p], fdof;
8007         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
8008 
8009         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
8010         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
8011         if (flip) {
8012           PetscInt i, j, k;
8013 
8014           if (!valCopy) {
8015             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8016             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
8017             *values = valCopy;
8018           }
8019           for (i = 0; i < fdof; ++i) {
8020             PetscScalar fval = flip[i];
8021 
8022             if (multiplyRight) {
8023               for (k = 0; k < nRows; ++k) { valCopy[Ni * k + (foffset + i)] *= fval; }
8024             }
8025             if (multiplyLeft) {
8026               for (k = 0; k < nCols; ++k) { valCopy[nCols * (foffset + i) + k] *= fval; }
8027             }
8028           }
8029         }
8030         foffset += fdof;
8031       }
8032     }
8033   }
8034   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
8035   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, Ncl, Ni, points, perms, nRows, nCols, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, multiplyRight, multiplyLeft));
8036   if (NclC) {
8037     if (multiplyRight) { *numCols = nCols = NiC; }
8038     if (multiplyLeft) { *numRows = nRows = NiC; }
8039     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8040     for (f = 0; f < PetscMax(1, Nf); ++f) {
8041       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8042       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8043     }
8044     for (f = 0; f < PetscMax(1, Nf); ++f) {
8045       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
8046       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
8047     }
8048     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8049     Ncl    = NclC;
8050     Ni     = NiC;
8051     points = pointsC;
8052     if (values) *values = valuesC;
8053   }
8054   /* 5) Calculate indices */
8055   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
8056   PetscCall(PetscSectionGetChart(idxSection, &idxStart, &idxEnd));
8057   if (Nf) {
8058     PetscInt  idxOff;
8059     PetscBool useFieldOffsets;
8060 
8061     if (outOffsets) {
8062       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
8063     }
8064     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
8065     if (useFieldOffsets) {
8066       for (p = 0; p < Ncl; ++p) {
8067         const PetscInt pnt = points[p * 2];
8068 
8069         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
8070       }
8071     } else {
8072       for (p = 0; p < Ncl; ++p) {
8073         const PetscInt pnt = points[p * 2];
8074 
8075         if (pnt < idxStart || pnt >= idxEnd) continue;
8076         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8077         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8078          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
8079          * global section. */
8080         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
8081       }
8082     }
8083   } else {
8084     PetscInt off = 0, idxOff;
8085 
8086     for (p = 0; p < Ncl; ++p) {
8087       const PetscInt  pnt  = points[p * 2];
8088       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
8089 
8090       if (pnt < idxStart || pnt >= idxEnd) continue;
8091       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8092       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8093        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
8094       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
8095     }
8096   }
8097   /* 6) Cleanup */
8098   for (f = 0; f < PetscMax(1, Nf); ++f) {
8099     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8100     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8101   }
8102   if (NclC) {
8103     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
8104   } else {
8105     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8106   }
8107 
8108   if (indices) *indices = idx;
8109   PetscFunctionReturn(PETSC_SUCCESS);
8110 }
8111 
8112 /*@C
8113   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
8114 
8115   Not collective
8116 
8117   Input Parameters:
8118 + dm         - The `DM`
8119 . section    - The `PetscSection` describing the points (a local section)
8120 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8121 . point      - The point defining the closure
8122 - useClPerm  - Use the closure point permutation if available
8123 
8124   Output Parameters:
8125 + numIndices - The number of dof indices in the closure of point with the input sections
8126 . indices    - The dof indices
8127 . outOffsets - Array to write the field offsets into, or `NULL`
8128 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8129 
8130   Level: advanced
8131 
8132   Notes:
8133   Call `DMPlexRestoreClosureIndices()` to free allocated memory
8134 
8135   If `idxSection` is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8136   of those indices is not significant.  If `idxSection` is local, the constrained dofs will yield the involution -(idx+1)
8137   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8138   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when `idxSection` == section, otherwise global
8139   indices (with the above semantics) are implied.
8140 
8141 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
8142           `PetscSection`, `DMGetGlobalSection()`
8143 @*/
8144 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
8145 {
8146   PetscInt numRows = -1, numCols = -1;
8147 
8148   PetscFunctionBeginHot;
8149   PetscCall(DMPlexGetClosureIndices_Internal(dm, section, idxSection, point, useClPerm, &numRows, &numCols, indices, outOffsets, values, PETSC_TRUE, PETSC_TRUE));
8150   PetscCheck(numRows == numCols, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Symmetric matrix transformation produces rectangular dimensions (%" PetscInt_FMT ", %" PetscInt_FMT ")", numRows, numCols);
8151   *numIndices = numRows;
8152   PetscFunctionReturn(PETSC_SUCCESS);
8153 }
8154 
8155 /*@C
8156   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
8157 
8158   Not collective
8159 
8160   Input Parameters:
8161 + dm         - The `DM`
8162 . section    - The `PetscSection` describing the points (a local section)
8163 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8164 . point      - The point defining the closure
8165 - useClPerm  - Use the closure point permutation if available
8166 
8167   Output Parameters:
8168 + numIndices - The number of dof indices in the closure of point with the input sections
8169 . indices    - The dof indices
8170 . outOffsets - Array to write the field offsets into, or `NULL`
8171 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8172 
8173   Level: advanced
8174 
8175   Notes:
8176   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
8177 
8178   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8179   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
8180   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8181   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
8182   indices (with the above semantics) are implied.
8183 
8184 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
8185 @*/
8186 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
8187 {
8188   PetscFunctionBegin;
8189   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8190   PetscAssertPointer(indices, 7);
8191   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
8192   PetscFunctionReturn(PETSC_SUCCESS);
8193 }
8194 
8195 PetscErrorCode DMPlexMatSetClosure_Internal(DM dm, PetscSection section, PetscSection globalSection, PetscBool useClPerm, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8196 {
8197   DM_Plex           *mesh = (DM_Plex *)dm->data;
8198   PetscInt          *indices;
8199   PetscInt           numIndices;
8200   const PetscScalar *valuesOrig = values;
8201   PetscErrorCode     ierr;
8202 
8203   PetscFunctionBegin;
8204   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8205   if (!section) PetscCall(DMGetLocalSection(dm, &section));
8206   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
8207   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
8208   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
8209   PetscValidHeaderSpecific(A, MAT_CLASSID, 5);
8210 
8211   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, useClPerm, &numIndices, &indices, NULL, (PetscScalar **)&values));
8212 
8213   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
8214   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8215   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
8216   if (ierr) {
8217     PetscMPIInt rank;
8218 
8219     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8220     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8221     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
8222     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8223     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8224     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
8225   }
8226   if (mesh->printFEM > 1) {
8227     PetscInt i;
8228     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
8229     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
8230     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8231   }
8232 
8233   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8234   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8235   PetscFunctionReturn(PETSC_SUCCESS);
8236 }
8237 
8238 /*@C
8239   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
8240 
8241   Not collective
8242 
8243   Input Parameters:
8244 + dm            - The `DM`
8245 . section       - The section describing the layout in `v`, or `NULL` to use the default section
8246 . globalSection - The section describing the layout in `v`, or `NULL` to use the default global section
8247 . A             - The matrix
8248 . point         - The point in the `DM`
8249 . values        - The array of values
8250 - mode          - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8251 
8252   Level: intermediate
8253 
8254 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8255 @*/
8256 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8257 {
8258   PetscFunctionBegin;
8259   PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, PETSC_TRUE, A, point, values, mode));
8260   PetscFunctionReturn(PETSC_SUCCESS);
8261 }
8262 
8263 /*@C
8264   DMPlexMatSetClosureGeneral - Set an array of the values on the closure of 'point' using a different row and column section
8265 
8266   Not collective
8267 
8268   Input Parameters:
8269 + dmRow            - The `DM` for the row fields
8270 . sectionRow       - The section describing the layout, or `NULL` to use the default section in `dmRow`
8271 . useRowPerm       - The flag to use the closure permutation of the `dmRow` if available
8272 . globalSectionRow - The section describing the layout, or `NULL` to use the default global section in `dmRow`
8273 . dmCol            - The `DM` for the column fields
8274 . sectionCol       - The section describing the layout, or `NULL` to use the default section in `dmCol`
8275 . useColPerm       - The flag to use the closure permutation of the `dmCol` if available
8276 . globalSectionCol - The section describing the layout, or `NULL` to use the default global section in `dmCol`
8277 . A                - The matrix
8278 . point            - The point in the `DM`
8279 . values           - The array of values
8280 - mode             - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8281 
8282   Level: intermediate
8283 
8284 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8285 @*/
8286 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)
8287 {
8288   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
8289   PetscInt          *indicesRow, *indicesCol;
8290   PetscInt           numIndicesRow = -1, numIndicesCol = -1;
8291   const PetscScalar *valuesV0 = values, *valuesV1, *valuesV2;
8292 
8293   PetscErrorCode ierr;
8294 
8295   PetscFunctionBegin;
8296   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
8297   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
8298   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
8299   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
8300   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
8301   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 5);
8302   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
8303   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 6);
8304   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
8305   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 7);
8306   PetscValidHeaderSpecific(A, MAT_CLASSID, 9);
8307 
8308   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmRow, sectionRow, point, &numIndicesRow));
8309   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmCol, sectionCol, point, &numIndicesCol));
8310   valuesV1 = valuesV0;
8311   PetscCall(DMPlexGetClosureIndices_Internal(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV1, PETSC_FALSE, PETSC_TRUE));
8312   valuesV2 = valuesV1;
8313   PetscCall(DMPlexGetClosureIndices_Internal(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesRow, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2, PETSC_TRUE, PETSC_FALSE));
8314 
8315   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2));
8316   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8317   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2, mode);
8318   if (ierr) {
8319     PetscMPIInt rank;
8320 
8321     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8322     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8323     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
8324     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV2));
8325     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8326     if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8327     if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8328   }
8329 
8330   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2));
8331   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8332   if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8333   if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8334   PetscFunctionReturn(PETSC_SUCCESS);
8335 }
8336 
8337 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8338 {
8339   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
8340   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8341   PetscInt       *cpoints = NULL;
8342   PetscInt       *findices, *cindices;
8343   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8344   PetscInt        foffsets[32], coffsets[32];
8345   DMPolytopeType  ct;
8346   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8347   PetscErrorCode  ierr;
8348 
8349   PetscFunctionBegin;
8350   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8351   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8352   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8353   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8354   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8355   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8356   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8357   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8358   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8359   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8360   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
8361   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8362   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8363   PetscCall(PetscArrayzero(foffsets, 32));
8364   PetscCall(PetscArrayzero(coffsets, 32));
8365   /* Column indices */
8366   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8367   maxFPoints = numCPoints;
8368   /* Compress out points not in the section */
8369   /*   TODO: Squeeze out points with 0 dof as well */
8370   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8371   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8372     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8373       cpoints[q * 2]     = cpoints[p];
8374       cpoints[q * 2 + 1] = cpoints[p + 1];
8375       ++q;
8376     }
8377   }
8378   numCPoints = q;
8379   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8380     PetscInt fdof;
8381 
8382     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8383     if (!dof) continue;
8384     for (f = 0; f < numFields; ++f) {
8385       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8386       coffsets[f + 1] += fdof;
8387     }
8388     numCIndices += dof;
8389   }
8390   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8391   /* Row indices */
8392   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8393   {
8394     DMPlexTransform tr;
8395     DMPolytopeType *rct;
8396     PetscInt       *rsize, *rcone, *rornt, Nt;
8397 
8398     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8399     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8400     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8401     numSubcells = rsize[Nt - 1];
8402     PetscCall(DMPlexTransformDestroy(&tr));
8403   }
8404   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8405   for (r = 0, q = 0; r < numSubcells; ++r) {
8406     /* TODO Map from coarse to fine cells */
8407     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8408     /* Compress out points not in the section */
8409     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8410     for (p = 0; p < numFPoints * 2; p += 2) {
8411       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8412         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8413         if (!dof) continue;
8414         for (s = 0; s < q; ++s)
8415           if (fpoints[p] == ftotpoints[s * 2]) break;
8416         if (s < q) continue;
8417         ftotpoints[q * 2]     = fpoints[p];
8418         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8419         ++q;
8420       }
8421     }
8422     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8423   }
8424   numFPoints = q;
8425   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8426     PetscInt fdof;
8427 
8428     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8429     if (!dof) continue;
8430     for (f = 0; f < numFields; ++f) {
8431       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8432       foffsets[f + 1] += fdof;
8433     }
8434     numFIndices += dof;
8435   }
8436   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8437 
8438   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8439   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8440   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8441   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8442   if (numFields) {
8443     const PetscInt **permsF[32] = {NULL};
8444     const PetscInt **permsC[32] = {NULL};
8445 
8446     for (f = 0; f < numFields; f++) {
8447       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8448       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8449     }
8450     for (p = 0; p < numFPoints; p++) {
8451       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8452       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8453     }
8454     for (p = 0; p < numCPoints; p++) {
8455       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8456       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8457     }
8458     for (f = 0; f < numFields; f++) {
8459       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8460       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8461     }
8462   } else {
8463     const PetscInt **permsF = NULL;
8464     const PetscInt **permsC = NULL;
8465 
8466     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8467     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8468     for (p = 0, off = 0; p < numFPoints; p++) {
8469       const PetscInt *perm = permsF ? permsF[p] : NULL;
8470 
8471       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8472       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8473     }
8474     for (p = 0, off = 0; p < numCPoints; p++) {
8475       const PetscInt *perm = permsC ? permsC[p] : NULL;
8476 
8477       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8478       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8479     }
8480     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8481     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8482   }
8483   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8484   /* TODO: flips */
8485   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8486   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8487   if (ierr) {
8488     PetscMPIInt rank;
8489 
8490     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8491     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8492     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8493     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8494     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8495   }
8496   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8497   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8498   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8499   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8500   PetscFunctionReturn(PETSC_SUCCESS);
8501 }
8502 
8503 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8504 {
8505   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8506   PetscInt       *cpoints      = NULL;
8507   PetscInt        foffsets[32] = {0}, coffsets[32] = {0};
8508   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8509   DMPolytopeType  ct;
8510   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8511 
8512   PetscFunctionBegin;
8513   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8514   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8515   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8516   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8517   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8518   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8519   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8520   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8521   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8522   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8523   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8524   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8525   /* Column indices */
8526   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8527   maxFPoints = numCPoints;
8528   /* Compress out points not in the section */
8529   /*   TODO: Squeeze out points with 0 dof as well */
8530   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8531   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8532     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8533       cpoints[q * 2]     = cpoints[p];
8534       cpoints[q * 2 + 1] = cpoints[p + 1];
8535       ++q;
8536     }
8537   }
8538   numCPoints = q;
8539   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8540     PetscInt fdof;
8541 
8542     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8543     if (!dof) continue;
8544     for (f = 0; f < numFields; ++f) {
8545       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8546       coffsets[f + 1] += fdof;
8547     }
8548     numCIndices += dof;
8549   }
8550   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8551   /* Row indices */
8552   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8553   {
8554     DMPlexTransform tr;
8555     DMPolytopeType *rct;
8556     PetscInt       *rsize, *rcone, *rornt, Nt;
8557 
8558     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8559     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8560     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8561     numSubcells = rsize[Nt - 1];
8562     PetscCall(DMPlexTransformDestroy(&tr));
8563   }
8564   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8565   for (r = 0, q = 0; r < numSubcells; ++r) {
8566     /* TODO Map from coarse to fine cells */
8567     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8568     /* Compress out points not in the section */
8569     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8570     for (p = 0; p < numFPoints * 2; p += 2) {
8571       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8572         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8573         if (!dof) continue;
8574         for (s = 0; s < q; ++s)
8575           if (fpoints[p] == ftotpoints[s * 2]) break;
8576         if (s < q) continue;
8577         ftotpoints[q * 2]     = fpoints[p];
8578         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8579         ++q;
8580       }
8581     }
8582     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8583   }
8584   numFPoints = q;
8585   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8586     PetscInt fdof;
8587 
8588     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8589     if (!dof) continue;
8590     for (f = 0; f < numFields; ++f) {
8591       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8592       foffsets[f + 1] += fdof;
8593     }
8594     numFIndices += dof;
8595   }
8596   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8597 
8598   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8599   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8600   if (numFields) {
8601     const PetscInt **permsF[32] = {NULL};
8602     const PetscInt **permsC[32] = {NULL};
8603 
8604     for (f = 0; f < numFields; f++) {
8605       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8606       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8607     }
8608     for (p = 0; p < numFPoints; p++) {
8609       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8610       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8611     }
8612     for (p = 0; p < numCPoints; p++) {
8613       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8614       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8615     }
8616     for (f = 0; f < numFields; f++) {
8617       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8618       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8619     }
8620   } else {
8621     const PetscInt **permsF = NULL;
8622     const PetscInt **permsC = NULL;
8623 
8624     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8625     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8626     for (p = 0, off = 0; p < numFPoints; p++) {
8627       const PetscInt *perm = permsF ? permsF[p] : NULL;
8628 
8629       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8630       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8631     }
8632     for (p = 0, off = 0; p < numCPoints; p++) {
8633       const PetscInt *perm = permsC ? permsC[p] : NULL;
8634 
8635       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8636       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8637     }
8638     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8639     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8640   }
8641   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8642   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8643   PetscFunctionReturn(PETSC_SUCCESS);
8644 }
8645 
8646 /*@
8647   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8648 
8649   Input Parameter:
8650 . dm - The `DMPLEX` object
8651 
8652   Output Parameter:
8653 . cellHeight - The height of a cell
8654 
8655   Level: developer
8656 
8657 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetVTKCellHeight()`
8658 @*/
8659 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8660 {
8661   DM_Plex *mesh = (DM_Plex *)dm->data;
8662 
8663   PetscFunctionBegin;
8664   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8665   PetscAssertPointer(cellHeight, 2);
8666   *cellHeight = mesh->vtkCellHeight;
8667   PetscFunctionReturn(PETSC_SUCCESS);
8668 }
8669 
8670 /*@
8671   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8672 
8673   Input Parameters:
8674 + dm         - The `DMPLEX` object
8675 - cellHeight - The height of a cell
8676 
8677   Level: developer
8678 
8679 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8680 @*/
8681 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8682 {
8683   DM_Plex *mesh = (DM_Plex *)dm->data;
8684 
8685   PetscFunctionBegin;
8686   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8687   mesh->vtkCellHeight = cellHeight;
8688   PetscFunctionReturn(PETSC_SUCCESS);
8689 }
8690 
8691 /*@
8692   DMPlexGetCellTypeStratum - Get the range of cells of a given celltype
8693 
8694   Input Parameters:
8695 + dm - The `DMPLEX` object
8696 - ct - The `DMPolytopeType` of the cell
8697 
8698   Output Parameters:
8699 + start - The first cell of this type, or `NULL`
8700 - end   - The upper bound on this celltype, or `NULL`
8701 
8702   Level: advanced
8703 
8704 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8705 @*/
8706 PetscErrorCode DMPlexGetCellTypeStratum(DM dm, DMPolytopeType ct, PetscInt *start, PetscInt *end)
8707 {
8708   DM_Plex *mesh = (DM_Plex *)dm->data;
8709   DMLabel  label;
8710   PetscInt pStart, pEnd;
8711 
8712   PetscFunctionBegin;
8713   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8714   if (start) {
8715     PetscAssertPointer(start, 3);
8716     *start = 0;
8717   }
8718   if (end) {
8719     PetscAssertPointer(end, 4);
8720     *end = 0;
8721   }
8722   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8723   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
8724   if (mesh->tr) {
8725     PetscCall(DMPlexTransformGetCellTypeStratum(mesh->tr, ct, start, end));
8726   } else {
8727     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
8728     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named celltype was found");
8729     PetscCall(DMLabelGetStratumBounds(label, ct, start, end));
8730   }
8731   PetscFunctionReturn(PETSC_SUCCESS);
8732 }
8733 
8734 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8735 {
8736   PetscSection section, globalSection;
8737   PetscInt    *numbers, p;
8738 
8739   PetscFunctionBegin;
8740   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8741   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8742   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8743   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8744   PetscCall(PetscSectionSetUp(section));
8745   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &globalSection));
8746   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8747   for (p = pStart; p < pEnd; ++p) {
8748     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8749     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8750     else numbers[p - pStart] += shift;
8751   }
8752   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8753   if (globalSize) {
8754     PetscLayout layout;
8755     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8756     PetscCall(PetscLayoutGetSize(layout, globalSize));
8757     PetscCall(PetscLayoutDestroy(&layout));
8758   }
8759   PetscCall(PetscSectionDestroy(&section));
8760   PetscCall(PetscSectionDestroy(&globalSection));
8761   PetscFunctionReturn(PETSC_SUCCESS);
8762 }
8763 
8764 /*@
8765   DMPlexCreateCellNumbering - Get a global cell numbering for all cells on this process
8766 
8767   Input Parameters:
8768 + dm         - The `DMPLEX` object
8769 - includeAll - Whether to include all cells, or just the simplex and box cells
8770 
8771   Output Parameter:
8772 . globalCellNumbers - Global cell numbers for all cells on this process
8773 
8774   Level: developer
8775 
8776 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`
8777 @*/
8778 PetscErrorCode DMPlexCreateCellNumbering(DM dm, PetscBool includeAll, IS *globalCellNumbers)
8779 {
8780   PetscInt cellHeight, cStart, cEnd;
8781 
8782   PetscFunctionBegin;
8783   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8784   if (includeAll) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8785   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8786   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8787   PetscFunctionReturn(PETSC_SUCCESS);
8788 }
8789 
8790 /*@
8791   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8792 
8793   Input Parameter:
8794 . dm - The `DMPLEX` object
8795 
8796   Output Parameter:
8797 . globalCellNumbers - Global cell numbers for all cells on this process
8798 
8799   Level: developer
8800 
8801 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateCellNumbering()`, `DMPlexGetVertexNumbering()`
8802 @*/
8803 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8804 {
8805   DM_Plex *mesh = (DM_Plex *)dm->data;
8806 
8807   PetscFunctionBegin;
8808   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8809   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8810   *globalCellNumbers = mesh->globalCellNumbers;
8811   PetscFunctionReturn(PETSC_SUCCESS);
8812 }
8813 
8814 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8815 {
8816   PetscInt vStart, vEnd;
8817 
8818   PetscFunctionBegin;
8819   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8820   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8821   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8822   PetscFunctionReturn(PETSC_SUCCESS);
8823 }
8824 
8825 /*@
8826   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8827 
8828   Input Parameter:
8829 . dm - The `DMPLEX` object
8830 
8831   Output Parameter:
8832 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8833 
8834   Level: developer
8835 
8836 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8837 @*/
8838 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8839 {
8840   DM_Plex *mesh = (DM_Plex *)dm->data;
8841 
8842   PetscFunctionBegin;
8843   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8844   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8845   *globalVertexNumbers = mesh->globalVertexNumbers;
8846   PetscFunctionReturn(PETSC_SUCCESS);
8847 }
8848 
8849 /*@
8850   DMPlexCreatePointNumbering - Create a global numbering for all points.
8851 
8852   Collective
8853 
8854   Input Parameter:
8855 . dm - The `DMPLEX` object
8856 
8857   Output Parameter:
8858 . globalPointNumbers - Global numbers for all points on this process
8859 
8860   Level: developer
8861 
8862   Notes:
8863   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8864   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8865   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8866   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8867 
8868   The partitioned mesh is
8869   ```
8870   (2)--0--(3)--1--(4)    (1)--0--(2)
8871   ```
8872   and its global numbering is
8873   ```
8874   (3)--0--(4)--1--(5)--2--(6)
8875   ```
8876   Then the global numbering is provided as
8877   ```
8878   [0] Number of indices in set 5
8879   [0] 0 0
8880   [0] 1 1
8881   [0] 2 3
8882   [0] 3 4
8883   [0] 4 -6
8884   [1] Number of indices in set 3
8885   [1] 0 2
8886   [1] 1 5
8887   [1] 2 6
8888   ```
8889 
8890 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8891 @*/
8892 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8893 {
8894   IS        nums[4];
8895   PetscInt  depths[4], gdepths[4], starts[4];
8896   PetscInt  depth, d, shift = 0;
8897   PetscBool empty = PETSC_FALSE;
8898 
8899   PetscFunctionBegin;
8900   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8901   PetscCall(DMPlexGetDepth(dm, &depth));
8902   // For unstratified meshes use dim instead of depth
8903   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8904   // If any stratum is empty, we must mark all empty
8905   for (d = 0; d <= depth; ++d) {
8906     PetscInt end;
8907 
8908     depths[d] = depth - d;
8909     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8910     if (!(starts[d] - end)) empty = PETSC_TRUE;
8911   }
8912   if (empty)
8913     for (d = 0; d <= depth; ++d) {
8914       depths[d] = -1;
8915       starts[d] = -1;
8916     }
8917   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8918   PetscCall(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8919   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]);
8920   // Note here that 'shift' is collective, so that the numbering is stratified by depth
8921   for (d = 0; d <= depth; ++d) {
8922     PetscInt pStart, pEnd, gsize;
8923 
8924     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8925     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8926     shift += gsize;
8927   }
8928   PetscCall(ISConcatenate(PETSC_COMM_SELF, depth + 1, nums, globalPointNumbers));
8929   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8930   PetscFunctionReturn(PETSC_SUCCESS);
8931 }
8932 
8933 /*@
8934   DMPlexCreateEdgeNumbering - Create a global numbering for edges.
8935 
8936   Collective
8937 
8938   Input Parameter:
8939 . dm - The `DMPLEX` object
8940 
8941   Output Parameter:
8942 . globalEdgeNumbers - Global numbers for all edges on this process
8943 
8944   Level: developer
8945 
8946   Notes:
8947   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).
8948 
8949 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`, `DMPlexCreatePointNumbering()`
8950 @*/
8951 PetscErrorCode DMPlexCreateEdgeNumbering(DM dm, IS *globalEdgeNumbers)
8952 {
8953   PetscSF  sf;
8954   PetscInt eStart, eEnd;
8955 
8956   PetscFunctionBegin;
8957   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8958   PetscCall(DMGetPointSF(dm, &sf));
8959   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
8960   PetscCall(DMPlexCreateNumbering_Plex(dm, eStart, eEnd, 0, NULL, sf, globalEdgeNumbers));
8961   PetscFunctionReturn(PETSC_SUCCESS);
8962 }
8963 
8964 /*@
8965   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8966 
8967   Input Parameter:
8968 . dm - The `DMPLEX` object
8969 
8970   Output Parameter:
8971 . ranks - The rank field
8972 
8973   Options Database Key:
8974 . -dm_partition_view - Adds the rank field into the `DM` output from `-dm_view` using the same viewer
8975 
8976   Level: intermediate
8977 
8978 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8979 @*/
8980 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8981 {
8982   DM             rdm;
8983   PetscFE        fe;
8984   PetscScalar   *r;
8985   PetscMPIInt    rank;
8986   DMPolytopeType ct;
8987   PetscInt       dim, cStart, cEnd, c;
8988   PetscBool      simplex;
8989 
8990   PetscFunctionBeginUser;
8991   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8992   PetscAssertPointer(ranks, 2);
8993   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
8994   PetscCall(DMClone(dm, &rdm));
8995   PetscCall(DMGetDimension(rdm, &dim));
8996   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8997   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8998   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
8999   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
9000   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
9001   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
9002   PetscCall(PetscFEDestroy(&fe));
9003   PetscCall(DMCreateDS(rdm));
9004   PetscCall(DMCreateGlobalVector(rdm, ranks));
9005   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
9006   PetscCall(VecGetArray(*ranks, &r));
9007   for (c = cStart; c < cEnd; ++c) {
9008     PetscScalar *lr;
9009 
9010     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
9011     if (lr) *lr = rank;
9012   }
9013   PetscCall(VecRestoreArray(*ranks, &r));
9014   PetscCall(DMDestroy(&rdm));
9015   PetscFunctionReturn(PETSC_SUCCESS);
9016 }
9017 
9018 /*@
9019   DMPlexCreateLabelField - Create a field whose value is the label value for that point
9020 
9021   Input Parameters:
9022 + dm    - The `DMPLEX`
9023 - label - The `DMLabel`
9024 
9025   Output Parameter:
9026 . val - The label value field
9027 
9028   Options Database Key:
9029 . -dm_label_view - Adds the label value field into the `DM` output from `-dm_view` using the same viewer
9030 
9031   Level: intermediate
9032 
9033 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
9034 @*/
9035 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
9036 {
9037   DM             rdm, plex;
9038   Vec            lval;
9039   PetscSection   section;
9040   PetscFE        fe;
9041   PetscScalar   *v;
9042   PetscInt       dim, pStart, pEnd, p, cStart;
9043   DMPolytopeType ct;
9044   char           name[PETSC_MAX_PATH_LEN];
9045   const char    *lname, *prefix;
9046 
9047   PetscFunctionBeginUser;
9048   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9049   PetscAssertPointer(label, 2);
9050   PetscAssertPointer(val, 3);
9051   PetscCall(DMClone(dm, &rdm));
9052   PetscCall(DMConvert(rdm, DMPLEX, &plex));
9053   PetscCall(DMPlexGetHeightStratum(plex, 0, &cStart, NULL));
9054   PetscCall(DMPlexGetCellType(plex, cStart, &ct));
9055   PetscCall(DMDestroy(&plex));
9056   PetscCall(DMGetDimension(rdm, &dim));
9057   PetscCall(DMGetOptionsPrefix(dm, &prefix));
9058   PetscCall(PetscObjectGetName((PetscObject)label, &lname));
9059   PetscCall(PetscSNPrintf(name, sizeof(name), "%s%s_", prefix ? prefix : "", lname));
9060   PetscCall(PetscFECreateByCell(PETSC_COMM_SELF, dim, 1, ct, name, -1, &fe));
9061   PetscCall(PetscObjectSetName((PetscObject)fe, ""));
9062   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
9063   PetscCall(PetscFEDestroy(&fe));
9064   PetscCall(DMCreateDS(rdm));
9065   PetscCall(DMCreateGlobalVector(rdm, val));
9066   PetscCall(DMCreateLocalVector(rdm, &lval));
9067   PetscCall(PetscObjectSetName((PetscObject)*val, lname));
9068   PetscCall(DMGetLocalSection(rdm, &section));
9069   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
9070   PetscCall(VecGetArray(lval, &v));
9071   for (p = pStart; p < pEnd; ++p) {
9072     PetscInt cval, dof, off;
9073 
9074     PetscCall(PetscSectionGetDof(section, p, &dof));
9075     if (!dof) continue;
9076     PetscCall(DMLabelGetValue(label, p, &cval));
9077     PetscCall(PetscSectionGetOffset(section, p, &off));
9078     for (PetscInt d = 0; d < dof; d++) v[off + d] = cval;
9079   }
9080   PetscCall(VecRestoreArray(lval, &v));
9081   PetscCall(DMLocalToGlobal(rdm, lval, INSERT_VALUES, *val));
9082   PetscCall(VecDestroy(&lval));
9083   PetscCall(DMDestroy(&rdm));
9084   PetscFunctionReturn(PETSC_SUCCESS);
9085 }
9086 
9087 /*@
9088   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
9089 
9090   Input Parameter:
9091 . dm - The `DMPLEX` object
9092 
9093   Level: developer
9094 
9095   Notes:
9096   This is a useful diagnostic when creating meshes programmatically.
9097 
9098   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9099 
9100 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9101 @*/
9102 PetscErrorCode DMPlexCheckSymmetry(DM dm)
9103 {
9104   PetscSection    coneSection, supportSection;
9105   const PetscInt *cone, *support;
9106   PetscInt        coneSize, c, supportSize, s;
9107   PetscInt        pStart, pEnd, p, pp, csize, ssize;
9108   PetscBool       storagecheck = PETSC_TRUE;
9109 
9110   PetscFunctionBegin;
9111   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9112   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
9113   PetscCall(DMPlexGetConeSection(dm, &coneSection));
9114   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
9115   /* Check that point p is found in the support of its cone points, and vice versa */
9116   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9117   for (p = pStart; p < pEnd; ++p) {
9118     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
9119     PetscCall(DMPlexGetCone(dm, p, &cone));
9120     for (c = 0; c < coneSize; ++c) {
9121       PetscBool dup = PETSC_FALSE;
9122       PetscInt  d;
9123       for (d = c - 1; d >= 0; --d) {
9124         if (cone[c] == cone[d]) {
9125           dup = PETSC_TRUE;
9126           break;
9127         }
9128       }
9129       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
9130       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
9131       for (s = 0; s < supportSize; ++s) {
9132         if (support[s] == p) break;
9133       }
9134       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
9135         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
9136         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
9137         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9138         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
9139         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
9140         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9141         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]);
9142         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
9143       }
9144     }
9145     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
9146     if (p != pp) {
9147       storagecheck = PETSC_FALSE;
9148       continue;
9149     }
9150     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
9151     PetscCall(DMPlexGetSupport(dm, p, &support));
9152     for (s = 0; s < supportSize; ++s) {
9153       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
9154       PetscCall(DMPlexGetCone(dm, support[s], &cone));
9155       for (c = 0; c < coneSize; ++c) {
9156         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
9157         if (cone[c] != pp) {
9158           c = 0;
9159           break;
9160         }
9161         if (cone[c] == p) break;
9162       }
9163       if (c >= coneSize) {
9164         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
9165         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
9166         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9167         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
9168         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
9169         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9170         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
9171       }
9172     }
9173   }
9174   if (storagecheck) {
9175     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
9176     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
9177     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
9178   }
9179   PetscFunctionReturn(PETSC_SUCCESS);
9180 }
9181 
9182 /*
9183   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.
9184 */
9185 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
9186 {
9187   DMPolytopeType  cct;
9188   PetscInt        ptpoints[4];
9189   const PetscInt *cone, *ccone, *ptcone;
9190   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
9191 
9192   PetscFunctionBegin;
9193   *unsplit = 0;
9194   switch (ct) {
9195   case DM_POLYTOPE_POINT_PRISM_TENSOR:
9196     ptpoints[npt++] = c;
9197     break;
9198   case DM_POLYTOPE_SEG_PRISM_TENSOR:
9199     PetscCall(DMPlexGetCone(dm, c, &cone));
9200     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9201     for (cp = 0; cp < coneSize; ++cp) {
9202       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
9203       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
9204     }
9205     break;
9206   case DM_POLYTOPE_TRI_PRISM_TENSOR:
9207   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9208     PetscCall(DMPlexGetCone(dm, c, &cone));
9209     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9210     for (cp = 0; cp < coneSize; ++cp) {
9211       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
9212       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
9213       for (ccp = 0; ccp < cconeSize; ++ccp) {
9214         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
9215         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
9216           PetscInt p;
9217           for (p = 0; p < npt; ++p)
9218             if (ptpoints[p] == ccone[ccp]) break;
9219           if (p == npt) ptpoints[npt++] = ccone[ccp];
9220         }
9221       }
9222     }
9223     break;
9224   default:
9225     break;
9226   }
9227   for (pt = 0; pt < npt; ++pt) {
9228     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
9229     if (ptcone[0] == ptcone[1]) ++(*unsplit);
9230   }
9231   PetscFunctionReturn(PETSC_SUCCESS);
9232 }
9233 
9234 /*@
9235   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
9236 
9237   Input Parameters:
9238 + dm         - The `DMPLEX` object
9239 - cellHeight - Normally 0
9240 
9241   Level: developer
9242 
9243   Notes:
9244   This is a useful diagnostic when creating meshes programmatically.
9245   Currently applicable only to homogeneous simplex or tensor meshes.
9246 
9247   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9248 
9249 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9250 @*/
9251 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
9252 {
9253   DMPlexInterpolatedFlag interp;
9254   DMPolytopeType         ct;
9255   PetscInt               vStart, vEnd, cStart, cEnd, c;
9256 
9257   PetscFunctionBegin;
9258   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9259   PetscCall(DMPlexIsInterpolated(dm, &interp));
9260   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9261   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9262   for (c = cStart; c < cEnd; ++c) {
9263     PetscInt *closure = NULL;
9264     PetscInt  coneSize, closureSize, cl, Nv = 0;
9265 
9266     PetscCall(DMPlexGetCellType(dm, c, &ct));
9267     if (ct == DM_POLYTOPE_UNKNOWN) continue;
9268     if (interp == DMPLEX_INTERPOLATED_FULL) {
9269       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9270       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));
9271     }
9272     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9273     for (cl = 0; cl < closureSize * 2; cl += 2) {
9274       const PetscInt p = closure[cl];
9275       if ((p >= vStart) && (p < vEnd)) ++Nv;
9276     }
9277     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9278     /* Special Case: Tensor faces with identified vertices */
9279     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
9280       PetscInt unsplit;
9281 
9282       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9283       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
9284     }
9285     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));
9286   }
9287   PetscFunctionReturn(PETSC_SUCCESS);
9288 }
9289 
9290 /*@
9291   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
9292 
9293   Collective
9294 
9295   Input Parameters:
9296 + dm         - The `DMPLEX` object
9297 - cellHeight - Normally 0
9298 
9299   Level: developer
9300 
9301   Notes:
9302   This is a useful diagnostic when creating meshes programmatically.
9303   This routine is only relevant for meshes that are fully interpolated across all ranks.
9304   It will error out if a partially interpolated mesh is given on some rank.
9305   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
9306 
9307   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9308 
9309 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
9310 @*/
9311 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
9312 {
9313   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
9314   DMPlexInterpolatedFlag interpEnum;
9315 
9316   PetscFunctionBegin;
9317   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9318   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
9319   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(PETSC_SUCCESS);
9320   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
9321     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported"));
9322     PetscFunctionReturn(PETSC_SUCCESS);
9323   }
9324 
9325   PetscCall(DMGetDimension(dm, &dim));
9326   PetscCall(DMPlexGetDepth(dm, &depth));
9327   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9328   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
9329     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
9330     for (c = cStart; c < cEnd; ++c) {
9331       const PetscInt       *cone, *ornt, *faceSizes, *faces;
9332       const DMPolytopeType *faceTypes;
9333       DMPolytopeType        ct;
9334       PetscInt              numFaces, coneSize, f;
9335       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
9336 
9337       PetscCall(DMPlexGetCellType(dm, c, &ct));
9338       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9339       if (unsplit) continue;
9340       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9341       PetscCall(DMPlexGetCone(dm, c, &cone));
9342       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
9343       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9344       for (cl = 0; cl < closureSize * 2; cl += 2) {
9345         const PetscInt p = closure[cl];
9346         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
9347       }
9348       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9349       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);
9350       for (f = 0; f < numFaces; ++f) {
9351         DMPolytopeType fct;
9352         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
9353 
9354         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
9355         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
9356         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
9357           const PetscInt p = fclosure[cl];
9358           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
9359         }
9360         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]);
9361         for (v = 0; v < fnumCorners; ++v) {
9362           if (fclosure[v] != faces[fOff + v]) {
9363             PetscInt v1;
9364 
9365             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
9366             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
9367             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
9368             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
9369             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9370             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]);
9371           }
9372         }
9373         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
9374         fOff += faceSizes[f];
9375       }
9376       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9377       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9378     }
9379   }
9380   PetscFunctionReturn(PETSC_SUCCESS);
9381 }
9382 
9383 /*@
9384   DMPlexCheckGeometry - Check the geometry of mesh cells
9385 
9386   Input Parameter:
9387 . dm - The `DMPLEX` object
9388 
9389   Level: developer
9390 
9391   Notes:
9392   This is a useful diagnostic when creating meshes programmatically.
9393 
9394   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9395 
9396 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9397 @*/
9398 PetscErrorCode DMPlexCheckGeometry(DM dm)
9399 {
9400   Vec       coordinates;
9401   PetscReal detJ, J[9], refVol = 1.0;
9402   PetscReal vol;
9403   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
9404 
9405   PetscFunctionBegin;
9406   PetscCall(DMGetDimension(dm, &dim));
9407   PetscCall(DMGetCoordinateDim(dm, &dE));
9408   if (dim != dE) PetscFunctionReturn(PETSC_SUCCESS);
9409   PetscCall(DMPlexGetDepth(dm, &depth));
9410   for (d = 0; d < dim; ++d) refVol *= 2.0;
9411   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9412   /* Make sure local coordinates are created, because that step is collective */
9413   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
9414   if (!coordinates) PetscFunctionReturn(PETSC_SUCCESS);
9415   for (c = cStart; c < cEnd; ++c) {
9416     DMPolytopeType ct;
9417     PetscInt       unsplit;
9418     PetscBool      ignoreZeroVol = PETSC_FALSE;
9419 
9420     PetscCall(DMPlexGetCellType(dm, c, &ct));
9421     switch (ct) {
9422     case DM_POLYTOPE_SEG_PRISM_TENSOR:
9423     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9424     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9425       ignoreZeroVol = PETSC_TRUE;
9426       break;
9427     default:
9428       break;
9429     }
9430     switch (ct) {
9431     case DM_POLYTOPE_TRI_PRISM:
9432     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9433     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9434     case DM_POLYTOPE_PYRAMID:
9435       continue;
9436     default:
9437       break;
9438     }
9439     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9440     if (unsplit) continue;
9441     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
9442     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);
9443     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
9444     /* This should work with periodicity since DG coordinates should be used */
9445     if (depth > 1) {
9446       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
9447       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);
9448       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
9449     }
9450   }
9451   PetscFunctionReturn(PETSC_SUCCESS);
9452 }
9453 
9454 /*@
9455   DMPlexCheckPointSF - Check that several necessary conditions are met for the point `PetscSF` of this plex.
9456 
9457   Collective
9458 
9459   Input Parameters:
9460 + dm              - The `DMPLEX` object
9461 . pointSF         - The `PetscSF`, or `NULL` for `PointSF` attached to `DM`
9462 - allowExtraRoots - Flag to allow extra points not present in the `DM`
9463 
9464   Level: developer
9465 
9466   Notes:
9467   This is mainly intended for debugging/testing purposes.
9468 
9469   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9470 
9471   Extra roots can come from periodic cuts, where additional points appear on the boundary
9472 
9473 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
9474 @*/
9475 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
9476 {
9477   PetscInt           l, nleaves, nroots, overlap;
9478   const PetscInt    *locals;
9479   const PetscSFNode *remotes;
9480   PetscBool          distributed;
9481   MPI_Comm           comm;
9482   PetscMPIInt        rank;
9483 
9484   PetscFunctionBegin;
9485   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9486   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
9487   else pointSF = dm->sf;
9488   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9489   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
9490   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9491   {
9492     PetscMPIInt mpiFlag;
9493 
9494     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
9495     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
9496   }
9497   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
9498   PetscCall(DMPlexIsDistributed(dm, &distributed));
9499   if (!distributed) {
9500     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);
9501     PetscFunctionReturn(PETSC_SUCCESS);
9502   }
9503   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);
9504   PetscCall(DMPlexGetOverlap(dm, &overlap));
9505 
9506   /* Check SF graph is compatible with DMPlex chart */
9507   {
9508     PetscInt pStart, pEnd, maxLeaf;
9509 
9510     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9511     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9512     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9513     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9514   }
9515 
9516   /* Check Point SF has no local points referenced */
9517   for (l = 0; l < nleaves; l++) {
9518     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);
9519   }
9520 
9521   /* Check there are no cells in interface */
9522   if (!overlap) {
9523     PetscInt cellHeight, cStart, cEnd;
9524 
9525     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9526     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9527     for (l = 0; l < nleaves; ++l) {
9528       const PetscInt point = locals ? locals[l] : l;
9529 
9530       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9531     }
9532   }
9533 
9534   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9535   {
9536     const PetscInt *rootdegree;
9537 
9538     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9539     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9540     for (l = 0; l < nleaves; ++l) {
9541       const PetscInt  point = locals ? locals[l] : l;
9542       const PetscInt *cone;
9543       PetscInt        coneSize, c, idx;
9544 
9545       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9546       PetscCall(DMPlexGetCone(dm, point, &cone));
9547       for (c = 0; c < coneSize; ++c) {
9548         if (!rootdegree[cone[c]]) {
9549           if (locals) {
9550             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9551           } else {
9552             idx = (cone[c] < nleaves) ? cone[c] : -1;
9553           }
9554           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9555         }
9556       }
9557     }
9558   }
9559   PetscFunctionReturn(PETSC_SUCCESS);
9560 }
9561 
9562 /*@
9563   DMPlexCheckOrphanVertices - Check that no vertices are disconnected from the mesh, unless the mesh only consists of disconnected vertices.
9564 
9565   Collective
9566 
9567   Input Parameter:
9568 . dm - The `DMPLEX` object
9569 
9570   Level: developer
9571 
9572   Notes:
9573   This is mainly intended for debugging/testing purposes.
9574 
9575   Other cell types which are disconnected would be caught by the symmetry and face checks.
9576 
9577   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9578 
9579 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheck()`, `DMSetFromOptions()`
9580 @*/
9581 PetscErrorCode DMPlexCheckOrphanVertices(DM dm)
9582 {
9583   PetscInt pStart, pEnd, vStart, vEnd;
9584 
9585   PetscFunctionBegin;
9586   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9587   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9588   if (pStart == vStart && pEnd == vEnd) PetscFunctionReturn(PETSC_SUCCESS);
9589   for (PetscInt v = vStart; v < vEnd; ++v) {
9590     PetscInt suppSize;
9591 
9592     PetscCall(DMPlexGetSupportSize(dm, v, &suppSize));
9593     PetscCheck(suppSize, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Vertex %" PetscInt_FMT " is disconnected from the mesh", v);
9594   }
9595   PetscFunctionReturn(PETSC_SUCCESS);
9596 }
9597 
9598 /*@
9599   DMPlexCheck - Perform various checks of `DMPLEX` sanity
9600 
9601   Input Parameter:
9602 . dm - The `DMPLEX` object
9603 
9604   Level: developer
9605 
9606   Notes:
9607   This is a useful diagnostic when creating meshes programmatically.
9608 
9609   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9610 
9611   Currently does not include `DMPlexCheckCellShape()`.
9612 
9613 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9614 @*/
9615 PetscErrorCode DMPlexCheck(DM dm)
9616 {
9617   PetscInt cellHeight;
9618 
9619   PetscFunctionBegin;
9620   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9621   PetscCall(DMPlexCheckSymmetry(dm));
9622   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9623   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9624   PetscCall(DMPlexCheckGeometry(dm));
9625   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9626   PetscCall(DMPlexCheckInterfaceCones(dm));
9627   PetscCall(DMPlexCheckOrphanVertices(dm));
9628   PetscFunctionReturn(PETSC_SUCCESS);
9629 }
9630 
9631 typedef struct cell_stats {
9632   PetscReal min, max, sum, squaresum;
9633   PetscInt  count;
9634 } cell_stats_t;
9635 
9636 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9637 {
9638   PetscInt i, N = *len;
9639 
9640   for (i = 0; i < N; i++) {
9641     cell_stats_t *A = (cell_stats_t *)a;
9642     cell_stats_t *B = (cell_stats_t *)b;
9643 
9644     B->min = PetscMin(A->min, B->min);
9645     B->max = PetscMax(A->max, B->max);
9646     B->sum += A->sum;
9647     B->squaresum += A->squaresum;
9648     B->count += A->count;
9649   }
9650 }
9651 
9652 /*@
9653   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9654 
9655   Collective
9656 
9657   Input Parameters:
9658 + dm        - The `DMPLEX` object
9659 . output    - If true, statistics will be displayed on `stdout`
9660 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9661 
9662   Level: developer
9663 
9664   Notes:
9665   This is mainly intended for debugging/testing purposes.
9666 
9667   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9668 
9669 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9670 @*/
9671 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9672 {
9673   DM           dmCoarse;
9674   cell_stats_t stats, globalStats;
9675   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9676   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9677   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9678   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9679   PetscMPIInt  rank, size;
9680 
9681   PetscFunctionBegin;
9682   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9683   stats.min = PETSC_MAX_REAL;
9684   stats.max = PETSC_MIN_REAL;
9685   stats.sum = stats.squaresum = 0.;
9686   stats.count                 = 0;
9687 
9688   PetscCallMPI(MPI_Comm_size(comm, &size));
9689   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9690   PetscCall(DMGetCoordinateDim(dm, &cdim));
9691   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9692   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9693   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9694   for (c = cStart; c < cEnd; c++) {
9695     PetscInt  i;
9696     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9697 
9698     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9699     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9700     for (i = 0; i < PetscSqr(cdim); ++i) {
9701       frobJ += J[i] * J[i];
9702       frobInvJ += invJ[i] * invJ[i];
9703     }
9704     cond2 = frobJ * frobInvJ;
9705     cond  = PetscSqrtReal(cond2);
9706 
9707     stats.min = PetscMin(stats.min, cond);
9708     stats.max = PetscMax(stats.max, cond);
9709     stats.sum += cond;
9710     stats.squaresum += cond2;
9711     stats.count++;
9712     if (output && cond > limit) {
9713       PetscSection coordSection;
9714       Vec          coordsLocal;
9715       PetscScalar *coords = NULL;
9716       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9717 
9718       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9719       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9720       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9721       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9722       for (i = 0; i < Nv / cdim; ++i) {
9723         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9724         for (d = 0; d < cdim; ++d) {
9725           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9726           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9727         }
9728         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9729       }
9730       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9731       for (cl = 0; cl < clSize * 2; cl += 2) {
9732         const PetscInt edge = closure[cl];
9733 
9734         if ((edge >= eStart) && (edge < eEnd)) {
9735           PetscReal len;
9736 
9737           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9738           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9739         }
9740       }
9741       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9742       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9743     }
9744   }
9745   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9746 
9747   if (size > 1) {
9748     PetscMPIInt  blockLengths[2] = {4, 1};
9749     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9750     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9751     MPI_Op       statReduce;
9752 
9753     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9754     PetscCallMPI(MPI_Type_commit(&statType));
9755     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9756     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9757     PetscCallMPI(MPI_Op_free(&statReduce));
9758     PetscCallMPI(MPI_Type_free(&statType));
9759   } else {
9760     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9761   }
9762   if (rank == 0) {
9763     count = globalStats.count;
9764     min   = globalStats.min;
9765     max   = globalStats.max;
9766     mean  = globalStats.sum / globalStats.count;
9767     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9768   }
9769 
9770   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));
9771   PetscCall(PetscFree2(J, invJ));
9772 
9773   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9774   if (dmCoarse) {
9775     PetscBool isplex;
9776 
9777     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9778     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9779   }
9780   PetscFunctionReturn(PETSC_SUCCESS);
9781 }
9782 
9783 /*@
9784   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9785   orthogonal quality below given tolerance.
9786 
9787   Collective
9788 
9789   Input Parameters:
9790 + dm   - The `DMPLEX` object
9791 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9792 - atol - [0, 1] Absolute tolerance for tagging cells.
9793 
9794   Output Parameters:
9795 + OrthQual      - `Vec` containing orthogonal quality per cell
9796 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9797 
9798   Options Database Keys:
9799 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9800 - -dm_plex_orthogonal_quality_vec_view   - view OrthQual vector.
9801 
9802   Level: intermediate
9803 
9804   Notes:
9805   Orthogonal quality is given by the following formula\:
9806 
9807   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9808 
9809   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
9810   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9811   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9812   calculating the cosine of the angle between these vectors.
9813 
9814   Orthogonal quality ranges from 1 (best) to 0 (worst).
9815 
9816   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9817   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9818 
9819   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9820 
9821 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9822 @*/
9823 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9824 {
9825   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9826   PetscInt              *idx;
9827   PetscScalar           *oqVals;
9828   const PetscScalar     *cellGeomArr, *faceGeomArr;
9829   PetscReal             *ci, *fi, *Ai;
9830   MPI_Comm               comm;
9831   Vec                    cellgeom, facegeom;
9832   DM                     dmFace, dmCell;
9833   IS                     glob;
9834   ISLocalToGlobalMapping ltog;
9835   PetscViewer            vwr;
9836 
9837   PetscFunctionBegin;
9838   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9839   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9840   PetscAssertPointer(OrthQual, 4);
9841   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9842   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9843   PetscCall(DMGetDimension(dm, &nc));
9844   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9845   {
9846     DMPlexInterpolatedFlag interpFlag;
9847 
9848     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9849     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9850       PetscMPIInt rank;
9851 
9852       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9853       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9854     }
9855   }
9856   if (OrthQualLabel) {
9857     PetscAssertPointer(OrthQualLabel, 5);
9858     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9859     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9860   } else {
9861     *OrthQualLabel = NULL;
9862   }
9863   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9864   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9865   PetscCall(DMPlexCreateCellNumbering(dm, PETSC_TRUE, &glob));
9866   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9867   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9868   PetscCall(VecCreate(comm, OrthQual));
9869   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9870   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9871   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9872   PetscCall(VecSetUp(*OrthQual));
9873   PetscCall(ISDestroy(&glob));
9874   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9875   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9876   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9877   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9878   PetscCall(VecGetDM(cellgeom, &dmCell));
9879   PetscCall(VecGetDM(facegeom, &dmFace));
9880   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9881   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
9882     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9883     PetscInt         cellarr[2], *adj = NULL;
9884     PetscScalar     *cArr, *fArr;
9885     PetscReal        minvalc = 1.0, minvalf = 1.0;
9886     PetscFVCellGeom *cg;
9887 
9888     idx[cellIter] = cell - cStart;
9889     cellarr[0]    = cell;
9890     /* Make indexing into cellGeom easier */
9891     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9892     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9893     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9894     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9895     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
9896       PetscInt         i;
9897       const PetscInt   neigh  = adj[cellneigh];
9898       PetscReal        normci = 0, normfi = 0, normai = 0;
9899       PetscFVCellGeom *cgneigh;
9900       PetscFVFaceGeom *fg;
9901 
9902       /* Don't count ourselves in the neighbor list */
9903       if (neigh == cell) continue;
9904       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9905       cellarr[1] = neigh;
9906       {
9907         PetscInt        numcovpts;
9908         const PetscInt *covpts;
9909 
9910         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9911         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9912         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9913       }
9914 
9915       /* Compute c_i, f_i and their norms */
9916       for (i = 0; i < nc; i++) {
9917         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9918         fi[i] = fg->centroid[i] - cg->centroid[i];
9919         Ai[i] = fg->normal[i];
9920         normci += PetscPowReal(ci[i], 2);
9921         normfi += PetscPowReal(fi[i], 2);
9922         normai += PetscPowReal(Ai[i], 2);
9923       }
9924       normci = PetscSqrtReal(normci);
9925       normfi = PetscSqrtReal(normfi);
9926       normai = PetscSqrtReal(normai);
9927 
9928       /* Normalize and compute for each face-cell-normal pair */
9929       for (i = 0; i < nc; i++) {
9930         ci[i] = ci[i] / normci;
9931         fi[i] = fi[i] / normfi;
9932         Ai[i] = Ai[i] / normai;
9933         /* PetscAbs because I don't know if normals are guaranteed to point out */
9934         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
9935         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
9936       }
9937       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
9938       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
9939     }
9940     PetscCall(PetscFree(adj));
9941     PetscCall(PetscFree2(cArr, fArr));
9942     /* Defer to cell if they're equal */
9943     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9944     if (OrthQualLabel) {
9945       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9946     }
9947   }
9948   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
9949   PetscCall(VecAssemblyBegin(*OrthQual));
9950   PetscCall(VecAssemblyEnd(*OrthQual));
9951   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9952   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9953   PetscCall(PetscOptionsCreateViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9954   if (OrthQualLabel) {
9955     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9956   }
9957   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9958   PetscCall(PetscViewerDestroy(&vwr));
9959   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9960   PetscFunctionReturn(PETSC_SUCCESS);
9961 }
9962 
9963 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9964  * interpolator construction */
9965 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9966 {
9967   PetscSection section, newSection, gsection;
9968   PetscSF      sf;
9969   PetscBool    hasConstraints, ghasConstraints;
9970 
9971   PetscFunctionBegin;
9972   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9973   PetscAssertPointer(odm, 2);
9974   PetscCall(DMGetLocalSection(dm, &section));
9975   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9976   PetscCall(MPIU_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
9977   if (!ghasConstraints) {
9978     PetscCall(PetscObjectReference((PetscObject)dm));
9979     *odm = dm;
9980     PetscFunctionReturn(PETSC_SUCCESS);
9981   }
9982   PetscCall(DMClone(dm, odm));
9983   PetscCall(DMCopyFields(dm, *odm));
9984   PetscCall(DMGetLocalSection(*odm, &newSection));
9985   PetscCall(DMGetPointSF(*odm, &sf));
9986   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_TRUE, PETSC_FALSE, &gsection));
9987   PetscCall(DMSetGlobalSection(*odm, gsection));
9988   PetscCall(PetscSectionDestroy(&gsection));
9989   PetscFunctionReturn(PETSC_SUCCESS);
9990 }
9991 
9992 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9993 {
9994   DM        dmco, dmfo;
9995   Mat       interpo;
9996   Vec       rscale;
9997   Vec       cglobalo, clocal;
9998   Vec       fglobal, fglobalo, flocal;
9999   PetscBool regular;
10000 
10001   PetscFunctionBegin;
10002   PetscCall(DMGetFullDM(dmc, &dmco));
10003   PetscCall(DMGetFullDM(dmf, &dmfo));
10004   PetscCall(DMSetCoarseDM(dmfo, dmco));
10005   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
10006   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
10007   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
10008   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
10009   PetscCall(DMCreateLocalVector(dmc, &clocal));
10010   PetscCall(VecSet(cglobalo, 0.));
10011   PetscCall(VecSet(clocal, 0.));
10012   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
10013   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
10014   PetscCall(DMCreateLocalVector(dmf, &flocal));
10015   PetscCall(VecSet(fglobal, 0.));
10016   PetscCall(VecSet(fglobalo, 0.));
10017   PetscCall(VecSet(flocal, 0.));
10018   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
10019   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
10020   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
10021   PetscCall(MatMult(interpo, cglobalo, fglobalo));
10022   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
10023   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
10024   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
10025   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
10026   *shift = fglobal;
10027   PetscCall(VecDestroy(&flocal));
10028   PetscCall(VecDestroy(&fglobalo));
10029   PetscCall(VecDestroy(&clocal));
10030   PetscCall(VecDestroy(&cglobalo));
10031   PetscCall(VecDestroy(&rscale));
10032   PetscCall(MatDestroy(&interpo));
10033   PetscCall(DMDestroy(&dmfo));
10034   PetscCall(DMDestroy(&dmco));
10035   PetscFunctionReturn(PETSC_SUCCESS);
10036 }
10037 
10038 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
10039 {
10040   PetscObject shifto;
10041   Vec         shift;
10042 
10043   PetscFunctionBegin;
10044   if (!interp) {
10045     Vec rscale;
10046 
10047     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
10048     PetscCall(VecDestroy(&rscale));
10049   } else {
10050     PetscCall(PetscObjectReference((PetscObject)interp));
10051   }
10052   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
10053   if (!shifto) {
10054     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
10055     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
10056     shifto = (PetscObject)shift;
10057     PetscCall(VecDestroy(&shift));
10058   }
10059   shift = (Vec)shifto;
10060   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
10061   PetscCall(VecAXPY(fineSol, 1.0, shift));
10062   PetscCall(MatDestroy(&interp));
10063   PetscFunctionReturn(PETSC_SUCCESS);
10064 }
10065 
10066 /* Pointwise interpolation
10067      Just code FEM for now
10068      u^f = I u^c
10069      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
10070      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
10071      I_{ij} = psi^f_i phi^c_j
10072 */
10073 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
10074 {
10075   PetscSection gsc, gsf;
10076   PetscInt     m, n;
10077   void        *ctx;
10078   DM           cdm;
10079   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
10080 
10081   PetscFunctionBegin;
10082   PetscCall(DMGetGlobalSection(dmFine, &gsf));
10083   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10084   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10085   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10086 
10087   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
10088   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
10089   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10090   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
10091   PetscCall(DMGetApplicationContext(dmFine, &ctx));
10092 
10093   PetscCall(DMGetCoarseDM(dmFine, &cdm));
10094   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10095   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
10096   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
10097   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
10098   if (scaling) {
10099     /* Use naive scaling */
10100     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
10101   }
10102   PetscFunctionReturn(PETSC_SUCCESS);
10103 }
10104 
10105 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
10106 {
10107   VecScatter ctx;
10108 
10109   PetscFunctionBegin;
10110   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
10111   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
10112   PetscCall(VecScatterDestroy(&ctx));
10113   PetscFunctionReturn(PETSC_SUCCESS);
10114 }
10115 
10116 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[])
10117 {
10118   const PetscInt f  = (PetscInt)PetscRealPart(constants[numConstants]);
10119   const PetscInt Nc = uOff[f + 1] - uOff[f];
10120   for (PetscInt c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
10121 }
10122 
10123 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
10124 {
10125   DM           dmc;
10126   PetscDS      ds;
10127   Vec          ones, locmass;
10128   IS           cellIS;
10129   PetscFormKey key;
10130   PetscInt     depth;
10131 
10132   PetscFunctionBegin;
10133   PetscCall(DMClone(dm, &dmc));
10134   PetscCall(DMCopyDisc(dm, dmc));
10135   PetscCall(DMGetDS(dmc, &ds));
10136   for (PetscInt f = 0; f < dmc->Nf; ++f) PetscCall(PetscDSSetJacobian(ds, f, f, g0_identity_private, NULL, NULL, NULL));
10137   PetscCall(DMCreateGlobalVector(dmc, mass));
10138   PetscCall(DMGetLocalVector(dmc, &ones));
10139   PetscCall(DMGetLocalVector(dmc, &locmass));
10140   PetscCall(DMPlexGetDepth(dmc, &depth));
10141   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
10142   PetscCall(VecSet(locmass, 0.0));
10143   PetscCall(VecSet(ones, 1.0));
10144   key.label = NULL;
10145   key.value = 0;
10146   key.field = 0;
10147   key.part  = 0;
10148   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
10149   PetscCall(ISDestroy(&cellIS));
10150   PetscCall(VecSet(*mass, 0.0));
10151   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
10152   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
10153   PetscCall(DMRestoreLocalVector(dmc, &ones));
10154   PetscCall(DMRestoreLocalVector(dmc, &locmass));
10155   PetscCall(DMDestroy(&dmc));
10156   PetscFunctionReturn(PETSC_SUCCESS);
10157 }
10158 
10159 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
10160 {
10161   PetscSection gsc, gsf;
10162   PetscInt     m, n;
10163   void        *ctx;
10164   DM           cdm;
10165   PetscBool    regular;
10166 
10167   PetscFunctionBegin;
10168   if (dmFine == dmCoarse) {
10169     DM            dmc;
10170     PetscDS       ds;
10171     PetscWeakForm wf;
10172     Vec           u;
10173     IS            cellIS;
10174     PetscFormKey  key;
10175     PetscInt      depth;
10176 
10177     PetscCall(DMClone(dmFine, &dmc));
10178     PetscCall(DMCopyDisc(dmFine, dmc));
10179     PetscCall(DMGetDS(dmc, &ds));
10180     PetscCall(PetscDSGetWeakForm(ds, &wf));
10181     PetscCall(PetscWeakFormClear(wf));
10182     for (PetscInt f = 0; f < dmc->Nf; ++f) PetscCall(PetscDSSetJacobian(ds, f, f, g0_identity_private, NULL, NULL, NULL));
10183     PetscCall(DMCreateMatrix(dmc, mass));
10184     PetscCall(DMGetLocalVector(dmc, &u));
10185     PetscCall(DMPlexGetDepth(dmc, &depth));
10186     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
10187     PetscCall(MatZeroEntries(*mass));
10188     key.label = NULL;
10189     key.value = 0;
10190     key.field = 0;
10191     key.part  = 0;
10192     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
10193     PetscCall(ISDestroy(&cellIS));
10194     PetscCall(DMRestoreLocalVector(dmc, &u));
10195     PetscCall(DMDestroy(&dmc));
10196   } else {
10197     PetscCall(DMGetGlobalSection(dmFine, &gsf));
10198     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10199     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10200     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10201 
10202     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
10203     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10204     PetscCall(MatSetType(*mass, dmCoarse->mattype));
10205     PetscCall(DMGetApplicationContext(dmFine, &ctx));
10206 
10207     PetscCall(DMGetCoarseDM(dmFine, &cdm));
10208     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10209     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
10210     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
10211   }
10212   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
10213   PetscFunctionReturn(PETSC_SUCCESS);
10214 }
10215 
10216 /*@
10217   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10218 
10219   Input Parameter:
10220 . dm - The `DMPLEX` object
10221 
10222   Output Parameter:
10223 . regular - The flag
10224 
10225   Level: intermediate
10226 
10227 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
10228 @*/
10229 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
10230 {
10231   PetscFunctionBegin;
10232   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10233   PetscAssertPointer(regular, 2);
10234   *regular = ((DM_Plex *)dm->data)->regularRefinement;
10235   PetscFunctionReturn(PETSC_SUCCESS);
10236 }
10237 
10238 /*@
10239   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10240 
10241   Input Parameters:
10242 + dm      - The `DMPLEX` object
10243 - regular - The flag
10244 
10245   Level: intermediate
10246 
10247 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
10248 @*/
10249 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
10250 {
10251   PetscFunctionBegin;
10252   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10253   ((DM_Plex *)dm->data)->regularRefinement = regular;
10254   PetscFunctionReturn(PETSC_SUCCESS);
10255 }
10256 
10257 /*@
10258   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
10259   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
10260 
10261   Not Collective
10262 
10263   Input Parameter:
10264 . dm - The `DMPLEX` object
10265 
10266   Output Parameters:
10267 + anchorSection - If not `NULL`, set to the section describing which points anchor the constrained points.
10268 - anchorIS      - If not `NULL`, set to the list of anchors indexed by `anchorSection`
10269 
10270   Level: intermediate
10271 
10272 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
10273 @*/
10274 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
10275 {
10276   DM_Plex *plex = (DM_Plex *)dm->data;
10277 
10278   PetscFunctionBegin;
10279   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10280   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
10281   if (anchorSection) *anchorSection = plex->anchorSection;
10282   if (anchorIS) *anchorIS = plex->anchorIS;
10283   PetscFunctionReturn(PETSC_SUCCESS);
10284 }
10285 
10286 /*@
10287   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.
10288 
10289   Collective
10290 
10291   Input Parameters:
10292 + dm            - The `DMPLEX` object
10293 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
10294                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10295 - anchorIS      - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10296 
10297   Level: intermediate
10298 
10299   Notes:
10300   Unlike boundary conditions, when a point's degrees of freedom in a section are constrained to
10301   an outside value, the anchor constraints set a point's degrees of freedom to be a linear
10302   combination of other points' degrees of freedom.
10303 
10304   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
10305   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
10306 
10307   The reference counts of `anchorSection` and `anchorIS` are incremented.
10308 
10309 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
10310 @*/
10311 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
10312 {
10313   DM_Plex    *plex = (DM_Plex *)dm->data;
10314   PetscMPIInt result;
10315 
10316   PetscFunctionBegin;
10317   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10318   if (anchorSection) {
10319     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
10320     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
10321     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
10322   }
10323   if (anchorIS) {
10324     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
10325     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
10326     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
10327   }
10328 
10329   PetscCall(PetscObjectReference((PetscObject)anchorSection));
10330   PetscCall(PetscSectionDestroy(&plex->anchorSection));
10331   plex->anchorSection = anchorSection;
10332 
10333   PetscCall(PetscObjectReference((PetscObject)anchorIS));
10334   PetscCall(ISDestroy(&plex->anchorIS));
10335   plex->anchorIS = anchorIS;
10336 
10337   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
10338     PetscInt        size, a, pStart, pEnd;
10339     const PetscInt *anchors;
10340 
10341     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10342     PetscCall(ISGetLocalSize(anchorIS, &size));
10343     PetscCall(ISGetIndices(anchorIS, &anchors));
10344     for (a = 0; a < size; a++) {
10345       PetscInt p;
10346 
10347       p = anchors[a];
10348       if (p >= pStart && p < pEnd) {
10349         PetscInt dof;
10350 
10351         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10352         if (dof) {
10353           PetscCall(ISRestoreIndices(anchorIS, &anchors));
10354           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
10355         }
10356       }
10357     }
10358     PetscCall(ISRestoreIndices(anchorIS, &anchors));
10359   }
10360   /* reset the generic constraints */
10361   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
10362   PetscFunctionReturn(PETSC_SUCCESS);
10363 }
10364 
10365 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
10366 {
10367   PetscSection anchorSection;
10368   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
10369 
10370   PetscFunctionBegin;
10371   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10372   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10373   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
10374   PetscCall(PetscSectionGetNumFields(section, &numFields));
10375   if (numFields) {
10376     PetscInt f;
10377     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
10378 
10379     for (f = 0; f < numFields; f++) {
10380       PetscInt numComp;
10381 
10382       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
10383       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
10384     }
10385   }
10386   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10387   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10388   pStart = PetscMax(pStart, sStart);
10389   pEnd   = PetscMin(pEnd, sEnd);
10390   pEnd   = PetscMax(pStart, pEnd);
10391   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
10392   for (p = pStart; p < pEnd; p++) {
10393     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10394     if (dof) {
10395       PetscCall(PetscSectionGetDof(section, p, &dof));
10396       PetscCall(PetscSectionSetDof(*cSec, p, dof));
10397       for (f = 0; f < numFields; f++) {
10398         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
10399         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
10400       }
10401     }
10402   }
10403   PetscCall(PetscSectionSetUp(*cSec));
10404   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
10405   PetscFunctionReturn(PETSC_SUCCESS);
10406 }
10407 
10408 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
10409 {
10410   PetscSection    aSec;
10411   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
10412   const PetscInt *anchors;
10413   PetscInt        numFields, f;
10414   IS              aIS;
10415   MatType         mtype;
10416   PetscBool       iscuda, iskokkos;
10417 
10418   PetscFunctionBegin;
10419   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10420   PetscCall(PetscSectionGetStorageSize(cSec, &m));
10421   PetscCall(PetscSectionGetStorageSize(section, &n));
10422   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
10423   PetscCall(MatSetSizes(*cMat, m, n, m, n));
10424   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
10425   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
10426   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
10427   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
10428   if (iscuda) mtype = MATSEQAIJCUSPARSE;
10429   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
10430   else mtype = MATSEQAIJ;
10431   PetscCall(MatSetType(*cMat, mtype));
10432   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
10433   PetscCall(ISGetIndices(aIS, &anchors));
10434   /* cSec will be a subset of aSec and section */
10435   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
10436   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10437   PetscCall(PetscMalloc1(m + 1, &i));
10438   i[0] = 0;
10439   PetscCall(PetscSectionGetNumFields(section, &numFields));
10440   for (p = pStart; p < pEnd; p++) {
10441     PetscInt rDof, rOff, r;
10442 
10443     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10444     if (!rDof) continue;
10445     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10446     if (numFields) {
10447       for (f = 0; f < numFields; f++) {
10448         annz = 0;
10449         for (r = 0; r < rDof; r++) {
10450           a = anchors[rOff + r];
10451           if (a < sStart || a >= sEnd) continue;
10452           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10453           annz += aDof;
10454         }
10455         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10456         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
10457         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10458       }
10459     } else {
10460       annz = 0;
10461       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10462       for (q = 0; q < dof; q++) {
10463         a = anchors[rOff + q];
10464         if (a < sStart || a >= sEnd) continue;
10465         PetscCall(PetscSectionGetDof(section, a, &aDof));
10466         annz += aDof;
10467       }
10468       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10469       PetscCall(PetscSectionGetOffset(cSec, p, &off));
10470       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10471     }
10472   }
10473   nnz = i[m];
10474   PetscCall(PetscMalloc1(nnz, &j));
10475   offset = 0;
10476   for (p = pStart; p < pEnd; p++) {
10477     if (numFields) {
10478       for (f = 0; f < numFields; f++) {
10479         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10480         for (q = 0; q < dof; q++) {
10481           PetscInt rDof, rOff, r;
10482           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10483           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10484           for (r = 0; r < rDof; r++) {
10485             PetscInt s;
10486 
10487             a = anchors[rOff + r];
10488             if (a < sStart || a >= sEnd) continue;
10489             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10490             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
10491             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10492           }
10493         }
10494       }
10495     } else {
10496       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10497       for (q = 0; q < dof; q++) {
10498         PetscInt rDof, rOff, r;
10499         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10500         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10501         for (r = 0; r < rDof; r++) {
10502           PetscInt s;
10503 
10504           a = anchors[rOff + r];
10505           if (a < sStart || a >= sEnd) continue;
10506           PetscCall(PetscSectionGetDof(section, a, &aDof));
10507           PetscCall(PetscSectionGetOffset(section, a, &aOff));
10508           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10509         }
10510       }
10511     }
10512   }
10513   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
10514   PetscCall(PetscFree(i));
10515   PetscCall(PetscFree(j));
10516   PetscCall(ISRestoreIndices(aIS, &anchors));
10517   PetscFunctionReturn(PETSC_SUCCESS);
10518 }
10519 
10520 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
10521 {
10522   DM_Plex     *plex = (DM_Plex *)dm->data;
10523   PetscSection anchorSection, section, cSec;
10524   Mat          cMat;
10525 
10526   PetscFunctionBegin;
10527   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10528   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10529   if (anchorSection) {
10530     PetscInt Nf;
10531 
10532     PetscCall(DMGetLocalSection(dm, &section));
10533     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
10534     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
10535     PetscCall(DMGetNumFields(dm, &Nf));
10536     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
10537     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
10538     PetscCall(PetscSectionDestroy(&cSec));
10539     PetscCall(MatDestroy(&cMat));
10540   }
10541   PetscFunctionReturn(PETSC_SUCCESS);
10542 }
10543 
10544 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10545 {
10546   IS           subis;
10547   PetscSection section, subsection;
10548 
10549   PetscFunctionBegin;
10550   PetscCall(DMGetLocalSection(dm, &section));
10551   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10552   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10553   /* Create subdomain */
10554   PetscCall(DMPlexFilter(dm, label, value, PETSC_FALSE, PETSC_FALSE, NULL, subdm));
10555   /* Create submodel */
10556   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10557   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10558   PetscCall(DMSetLocalSection(*subdm, subsection));
10559   PetscCall(PetscSectionDestroy(&subsection));
10560   PetscCall(DMCopyDisc(dm, *subdm));
10561   /* Create map from submodel to global model */
10562   if (is) {
10563     PetscSection    sectionGlobal, subsectionGlobal;
10564     IS              spIS;
10565     const PetscInt *spmap;
10566     PetscInt       *subIndices;
10567     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10568     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10569 
10570     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10571     PetscCall(ISGetIndices(spIS, &spmap));
10572     PetscCall(PetscSectionGetNumFields(section, &Nf));
10573     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10574     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10575     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10576     for (p = pStart; p < pEnd; ++p) {
10577       PetscInt gdof, pSubSize = 0;
10578 
10579       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10580       if (gdof > 0) {
10581         for (f = 0; f < Nf; ++f) {
10582           PetscInt fdof, fcdof;
10583 
10584           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10585           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10586           pSubSize += fdof - fcdof;
10587         }
10588         subSize += pSubSize;
10589         if (pSubSize) {
10590           if (bs < 0) {
10591             bs = pSubSize;
10592           } else if (bs != pSubSize) {
10593             /* Layout does not admit a pointwise block size */
10594             bs = 1;
10595           }
10596         }
10597       }
10598     }
10599     /* Must have same blocksize on all procs (some might have no points) */
10600     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
10601     bsLocal[1] = bs;
10602     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10603     if (bsMinMax[0] != bsMinMax[1]) {
10604       bs = 1;
10605     } else {
10606       bs = bsMinMax[0];
10607     }
10608     PetscCall(PetscMalloc1(subSize, &subIndices));
10609     for (p = pStart; p < pEnd; ++p) {
10610       PetscInt gdof, goff;
10611 
10612       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10613       if (gdof > 0) {
10614         const PetscInt point = spmap[p];
10615 
10616         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10617         for (f = 0; f < Nf; ++f) {
10618           PetscInt fdof, fcdof, fc, f2, poff = 0;
10619 
10620           /* Can get rid of this loop by storing field information in the global section */
10621           for (f2 = 0; f2 < f; ++f2) {
10622             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10623             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10624             poff += fdof - fcdof;
10625           }
10626           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10627           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10628           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10629         }
10630       }
10631     }
10632     PetscCall(ISRestoreIndices(spIS, &spmap));
10633     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10634     if (bs > 1) {
10635       /* We need to check that the block size does not come from non-contiguous fields */
10636       PetscInt i, j, set = 1;
10637       for (i = 0; i < subSize; i += bs) {
10638         for (j = 0; j < bs; ++j) {
10639           if (subIndices[i + j] != subIndices[i] + j) {
10640             set = 0;
10641             break;
10642           }
10643         }
10644       }
10645       if (set) PetscCall(ISSetBlockSize(*is, bs));
10646     }
10647     /* Attach nullspace */
10648     for (f = 0; f < Nf; ++f) {
10649       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10650       if ((*subdm)->nullspaceConstructors[f]) break;
10651     }
10652     if (f < Nf) {
10653       MatNullSpace nullSpace;
10654       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10655 
10656       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10657       PetscCall(MatNullSpaceDestroy(&nullSpace));
10658     }
10659   }
10660   PetscFunctionReturn(PETSC_SUCCESS);
10661 }
10662 
10663 /*@
10664   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10665 
10666   Input Parameters:
10667 + dm    - The `DM`
10668 - dummy - unused argument
10669 
10670   Options Database Key:
10671 . -dm_plex_monitor_throughput - Activate the monitor
10672 
10673   Level: developer
10674 
10675 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10676 @*/
10677 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10678 {
10679   PetscLogHandler default_handler;
10680 
10681   PetscFunctionBegin;
10682   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10683   PetscCall(PetscLogGetDefaultHandler(&default_handler));
10684   if (default_handler) {
10685     PetscLogEvent      event;
10686     PetscEventPerfInfo eventInfo;
10687     PetscReal          cellRate, flopRate;
10688     PetscInt           cStart, cEnd, Nf, N;
10689     const char        *name;
10690 
10691     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10692     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10693     PetscCall(DMGetNumFields(dm, &Nf));
10694     PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10695     PetscCall(PetscLogEventGetPerfInfo(PETSC_DEFAULT, event, &eventInfo));
10696     N        = (cEnd - cStart) * Nf * eventInfo.count;
10697     flopRate = eventInfo.flops / eventInfo.time;
10698     cellRate = N / eventInfo.time;
10699     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)));
10700   } else {
10701     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.");
10702   }
10703   PetscFunctionReturn(PETSC_SUCCESS);
10704 }
10705