xref: /petsc/src/dm/impls/plex/plex.c (revision 0dd791a82873e11d89e43490eb85393c825f807f)
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 
12 /* Logging support */
13 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;
14 PetscLogEvent DMPLEX_RebalBuildGraph, DMPLEX_RebalRewriteSF, DMPLEX_RebalGatherGraph, DMPLEX_RebalPartition, DMPLEX_RebalScatterPart, DMPLEX_Generate, DMPLEX_Transform, DMPLEX_GetLocalOffsets, DMPLEX_Uninterpolate;
15 
16 PetscBool  Plexcite       = PETSC_FALSE;
17 const char PlexCitation[] = "@article{LangeMitchellKnepleyGorman2015,\n"
18                             "title     = {Efficient mesh management in {Firedrake} using {PETSc-DMPlex}},\n"
19                             "author    = {Michael Lange and Lawrence Mitchell and Matthew G. Knepley and Gerard J. Gorman},\n"
20                             "journal   = {SIAM Journal on Scientific Computing},\n"
21                             "volume    = {38},\n"
22                             "number    = {5},\n"
23                             "pages     = {S143--S155},\n"
24                             "eprint    = {http://arxiv.org/abs/1506.07749},\n"
25                             "doi       = {10.1137/15M1026092},\n"
26                             "year      = {2016},\n"
27                             "petsc_uses={DMPlex},\n}\n";
28 
29 PETSC_EXTERN PetscErrorCode VecView_MPI(Vec, PetscViewer);
30 
31 /*@
32   DMPlexIsSimplex - Is the first cell in this mesh a simplex?
33 
34   Input Parameter:
35 . dm - The `DMPLEX` object
36 
37   Output Parameter:
38 . simplex - Flag checking for a simplex
39 
40   Level: intermediate
41 
42   Note:
43   This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
44   If the mesh has no cells, this returns `PETSC_FALSE`.
45 
46 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSimplexOrBoxCells()`, `DMPlexGetCellType()`, `DMPlexGetHeightStratum()`, `DMPolytopeTypeGetNumVertices()`
47 @*/
48 PetscErrorCode DMPlexIsSimplex(DM dm, PetscBool *simplex)
49 {
50   DMPolytopeType ct;
51   PetscInt       cStart, cEnd;
52 
53   PetscFunctionBegin;
54   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
55   if (cEnd <= cStart) {
56     *simplex = PETSC_FALSE;
57     PetscFunctionReturn(PETSC_SUCCESS);
58   }
59   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
60   *simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
61   PetscFunctionReturn(PETSC_SUCCESS);
62 }
63 
64 /*@
65   DMPlexGetSimplexOrBoxCells - Get the range of cells which are neither prisms nor ghost FV cells
66 
67   Input Parameters:
68 + dm     - The `DMPLEX` object
69 - height - The cell height in the Plex, 0 is the default
70 
71   Output Parameters:
72 + cStart - The first "normal" cell
73 - cEnd   - The upper bound on "normal" cells
74 
75   Level: developer
76 
77   Note:
78   This function requires that tensor cells are ordered last.
79 
80 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetCellTypeStratum()`
81 @*/
82 PetscErrorCode DMPlexGetSimplexOrBoxCells(DM dm, PetscInt height, PetscInt *cStart, PetscInt *cEnd)
83 {
84   DMLabel         ctLabel;
85   IS              valueIS;
86   const PetscInt *ctypes;
87   PetscInt        Nct, cS = PETSC_MAX_INT, cE = 0;
88 
89   PetscFunctionBegin;
90   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
91   PetscCall(DMLabelGetValueIS(ctLabel, &valueIS));
92   PetscCall(ISGetLocalSize(valueIS, &Nct));
93   PetscCall(ISGetIndices(valueIS, &ctypes));
94   if (!Nct) cS = cE = 0;
95   for (PetscInt t = 0; t < Nct; ++t) {
96     const DMPolytopeType ct = (DMPolytopeType)ctypes[t];
97     PetscInt             ctS, ctE, ht;
98 
99     if (ct == DM_POLYTOPE_UNKNOWN) {
100       // If any cells are not typed, just use all cells
101       PetscCall(DMPlexGetHeightStratum(dm, PetscMax(height, 0), cStart, cEnd));
102       break;
103     }
104     if (DMPolytopeTypeIsHybrid(ct) || ct == DM_POLYTOPE_FV_GHOST) continue;
105     PetscCall(DMLabelGetStratumBounds(ctLabel, ct, &ctS, &ctE));
106     if (ctS >= ctE) continue;
107     // Check that a point has the right height
108     PetscCall(DMPlexGetPointHeight(dm, ctS, &ht));
109     if (ht != height) continue;
110     cS = PetscMin(cS, ctS);
111     cE = PetscMax(cE, ctE);
112   }
113   PetscCall(ISDestroy(&valueIS));
114   // Reset label for fast lookup
115   PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
116   if (cStart) *cStart = cS;
117   if (cEnd) *cEnd = cE;
118   PetscFunctionReturn(PETSC_SUCCESS);
119 }
120 
121 PetscErrorCode DMPlexGetFieldTypes_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *types, PetscInt **ssStart, PetscInt **ssEnd, PetscViewerVTKFieldType **sft)
122 {
123   PetscInt                 cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd, c, depth, cellHeight, t;
124   PetscInt                *sStart, *sEnd;
125   PetscViewerVTKFieldType *ft;
126   PetscInt                 vcdof[DM_NUM_POLYTOPES + 1], globalvcdof[DM_NUM_POLYTOPES + 1];
127   DMLabel                  depthLabel, ctLabel;
128 
129   PetscFunctionBegin;
130   /* the vcdof and globalvcdof are sized to allow every polytope type and simple vertex at DM_NUM_POLYTOPES */
131   PetscCall(PetscArrayzero(vcdof, DM_NUM_POLYTOPES + 1));
132   PetscCall(DMGetCoordinateDim(dm, &cdim));
133   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
134   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
135   if (field >= 0) {
136     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[DM_NUM_POLYTOPES]));
137   } else {
138     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[DM_NUM_POLYTOPES]));
139   }
140 
141   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
142   PetscCall(DMPlexGetDepth(dm, &depth));
143   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
144   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
145   for (c = 0; c < DM_NUM_POLYTOPES; ++c) {
146     const DMPolytopeType ict = (DMPolytopeType)c;
147     PetscInt             dep;
148 
149     if (ict == DM_POLYTOPE_FV_GHOST) continue;
150     PetscCall(DMLabelGetStratumBounds(ctLabel, ict, &cStart, &cEnd));
151     if (pStart >= 0) {
152       PetscCall(DMLabelGetValue(depthLabel, cStart, &dep));
153       if (dep != depth - cellHeight) continue;
154     }
155     if (field >= 0) {
156       if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[c]));
157     } else {
158       if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[c]));
159     }
160   }
161 
162   PetscCall(MPIU_Allreduce(vcdof, globalvcdof, DM_NUM_POLYTOPES + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
163   *types = 0;
164 
165   for (c = 0; c < DM_NUM_POLYTOPES + 1; ++c) {
166     if (globalvcdof[c]) ++(*types);
167   }
168 
169   PetscCall(PetscMalloc3(*types, &sStart, *types, &sEnd, *types, &ft));
170   t = 0;
171   if (globalvcdof[DM_NUM_POLYTOPES]) {
172     sStart[t] = vStart;
173     sEnd[t]   = vEnd;
174     ft[t]     = (globalvcdof[t] == cdim) ? PETSC_VTK_POINT_VECTOR_FIELD : PETSC_VTK_POINT_FIELD;
175     ++t;
176   }
177 
178   for (c = 0; c < DM_NUM_POLYTOPES; ++c) {
179     if (globalvcdof[c]) {
180       const DMPolytopeType ict = (DMPolytopeType)c;
181 
182       PetscCall(DMLabelGetStratumBounds(ctLabel, ict, &cStart, &cEnd));
183       sStart[t] = cStart;
184       sEnd[t]   = cEnd;
185       ft[t]     = (globalvcdof[c] == cdim) ? PETSC_VTK_CELL_VECTOR_FIELD : PETSC_VTK_CELL_FIELD;
186       ++t;
187     }
188   }
189 
190   if (!*types) {
191     if (field >= 0) {
192       const char *fieldname;
193 
194       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
195       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
196     } else {
197       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
198     }
199   }
200 
201   *ssStart = sStart;
202   *ssEnd   = sEnd;
203   *sft     = ft;
204   PetscFunctionReturn(PETSC_SUCCESS);
205 }
206 
207 PetscErrorCode DMPlexRestoreFieldTypes_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *types, PetscInt **sStart, PetscInt **sEnd, PetscViewerVTKFieldType **ft)
208 {
209   PetscFunctionBegin;
210   PetscCall(PetscFree3(*sStart, *sEnd, *ft));
211   PetscFunctionReturn(PETSC_SUCCESS);
212 }
213 
214 PetscErrorCode DMPlexGetFieldType_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *sStart, PetscInt *sEnd, PetscViewerVTKFieldType *ft)
215 {
216   PetscInt cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd;
217   PetscInt vcdof[2] = {0, 0}, globalvcdof[2];
218 
219   PetscFunctionBegin;
220   *ft = PETSC_VTK_INVALID;
221   PetscCall(DMGetCoordinateDim(dm, &cdim));
222   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
223   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
224   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
225   if (field >= 0) {
226     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[0]));
227     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[1]));
228   } else {
229     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[0]));
230     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[1]));
231   }
232   PetscCall(MPIU_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
233   if (globalvcdof[0]) {
234     *sStart = vStart;
235     *sEnd   = vEnd;
236     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
237     else *ft = PETSC_VTK_POINT_FIELD;
238   } else if (globalvcdof[1]) {
239     *sStart = cStart;
240     *sEnd   = cEnd;
241     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
242     else *ft = PETSC_VTK_CELL_FIELD;
243   } else {
244     if (field >= 0) {
245       const char *fieldname;
246 
247       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
248       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
249     } else {
250       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
251     }
252   }
253   PetscFunctionReturn(PETSC_SUCCESS);
254 }
255 
256 /*@
257   DMPlexVecView1D - Plot many 1D solutions on the same line graph
258 
259   Collective
260 
261   Input Parameters:
262 + dm     - The `DMPLEX` object
263 . n      - The number of vectors
264 . u      - The array of local vectors
265 - viewer - The `PetscViewer`
266 
267   Level: advanced
268 
269 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `VecViewFromOptions()`, `VecView()`
270 @*/
271 PetscErrorCode DMPlexVecView1D(DM dm, PetscInt n, Vec u[], PetscViewer viewer)
272 {
273   PetscDS            ds;
274   PetscDraw          draw = NULL;
275   PetscDrawLG        lg;
276   Vec                coordinates;
277   const PetscScalar *coords, **sol;
278   PetscReal         *vals;
279   PetscInt          *Nc;
280   PetscInt           Nf, f, c, Nl, l, i, vStart, vEnd, v;
281   char             **names;
282 
283   PetscFunctionBegin;
284   PetscCall(DMGetDS(dm, &ds));
285   PetscCall(PetscDSGetNumFields(ds, &Nf));
286   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
287   PetscCall(PetscDSGetComponents(ds, &Nc));
288 
289   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
290   if (!draw) PetscFunctionReturn(PETSC_SUCCESS);
291   PetscCall(PetscDrawLGCreate(draw, n * Nl, &lg));
292 
293   PetscCall(PetscMalloc3(n, &sol, n * Nl, &names, n * Nl, &vals));
294   for (i = 0, l = 0; i < n; ++i) {
295     const char *vname;
296 
297     PetscCall(PetscObjectGetName((PetscObject)u[i], &vname));
298     for (f = 0; f < Nf; ++f) {
299       PetscObject disc;
300       const char *fname;
301       char        tmpname[PETSC_MAX_PATH_LEN];
302 
303       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
304       /* TODO Create names for components */
305       for (c = 0; c < Nc[f]; ++c, ++l) {
306         PetscCall(PetscObjectGetName(disc, &fname));
307         PetscCall(PetscStrncpy(tmpname, vname, sizeof(tmpname)));
308         PetscCall(PetscStrlcat(tmpname, ":", sizeof(tmpname)));
309         PetscCall(PetscStrlcat(tmpname, fname, sizeof(tmpname)));
310         PetscCall(PetscStrallocpy(tmpname, &names[l]));
311       }
312     }
313   }
314   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *)names));
315   /* Just add P_1 support for now */
316   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
317   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
318   PetscCall(VecGetArrayRead(coordinates, &coords));
319   for (i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
320   for (v = vStart; v < vEnd; ++v) {
321     PetscScalar *x, *svals;
322 
323     PetscCall(DMPlexPointLocalRead(dm, v, coords, &x));
324     for (i = 0; i < n; ++i) {
325       PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
326       for (l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
327     }
328     PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
329   }
330   PetscCall(VecRestoreArrayRead(coordinates, &coords));
331   for (i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
332   for (l = 0; l < n * Nl; ++l) PetscCall(PetscFree(names[l]));
333   PetscCall(PetscFree3(sol, names, vals));
334 
335   PetscCall(PetscDrawLGDraw(lg));
336   PetscCall(PetscDrawLGDestroy(&lg));
337   PetscFunctionReturn(PETSC_SUCCESS);
338 }
339 
340 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer)
341 {
342   DM dm;
343 
344   PetscFunctionBegin;
345   PetscCall(VecGetDM(u, &dm));
346   PetscCall(DMPlexVecView1D(dm, 1, &u, viewer));
347   PetscFunctionReturn(PETSC_SUCCESS);
348 }
349 
350 static PetscErrorCode VecView_Plex_Local_Draw_2D(Vec v, PetscViewer viewer)
351 {
352   DM                 dm;
353   PetscSection       s;
354   PetscDraw          draw, popup;
355   DM                 cdm;
356   PetscSection       coordSection;
357   Vec                coordinates;
358   const PetscScalar *array;
359   PetscReal          lbound[3], ubound[3];
360   PetscReal          vbound[2], time;
361   PetscBool          flg;
362   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
363   const char        *name;
364   char               title[PETSC_MAX_PATH_LEN];
365 
366   PetscFunctionBegin;
367   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
368   PetscCall(VecGetDM(v, &dm));
369   PetscCall(DMGetCoordinateDim(dm, &dim));
370   PetscCall(DMGetLocalSection(dm, &s));
371   PetscCall(PetscSectionGetNumFields(s, &Nf));
372   PetscCall(DMGetCoarsenLevel(dm, &level));
373   PetscCall(DMGetCoordinateDM(dm, &cdm));
374   PetscCall(DMGetLocalSection(cdm, &coordSection));
375   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
376   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
377   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
378 
379   PetscCall(PetscObjectGetName((PetscObject)v, &name));
380   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
381 
382   PetscCall(VecGetLocalSize(coordinates, &N));
383   PetscCall(DMGetBoundingBox(dm, lbound, ubound));
384   PetscCall(PetscDrawClear(draw));
385 
386   /* Could implement something like DMDASelectFields() */
387   for (f = 0; f < Nf; ++f) {
388     DM          fdm = dm;
389     Vec         fv  = v;
390     IS          fis;
391     char        prefix[PETSC_MAX_PATH_LEN];
392     const char *fname;
393 
394     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
395     PetscCall(PetscSectionGetFieldName(s, f, &fname));
396 
397     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix, sizeof(prefix)));
398     else prefix[0] = '\0';
399     if (Nf > 1) {
400       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
401       PetscCall(VecGetSubVector(v, fis, &fv));
402       PetscCall(PetscStrlcat(prefix, fname, sizeof(prefix)));
403       PetscCall(PetscStrlcat(prefix, "_", sizeof(prefix)));
404     }
405     for (comp = 0; comp < Nc; ++comp, ++w) {
406       PetscInt nmax = 2;
407 
408       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
409       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
410       else PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
411       PetscCall(PetscDrawSetTitle(draw, title));
412 
413       /* TODO Get max and min only for this component */
414       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
415       if (!flg) {
416         PetscCall(VecMin(fv, NULL, &vbound[0]));
417         PetscCall(VecMax(fv, NULL, &vbound[1]));
418         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
419       }
420 
421       PetscCall(PetscDrawGetPopup(draw, &popup));
422       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
423       PetscCall(PetscDrawSetCoordinates(draw, lbound[0], lbound[1], ubound[0], ubound[1]));
424       PetscCall(VecGetArrayRead(fv, &array));
425       for (c = cStart; c < cEnd; ++c) {
426         PetscScalar       *coords = NULL, *a = NULL;
427         const PetscScalar *coords_arr;
428         PetscBool          isDG;
429         PetscInt           numCoords, color[4] = {-1, -1, -1, -1};
430 
431         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
432         if (a) {
433           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
434           color[1] = color[2] = color[3] = color[0];
435         } else {
436           PetscScalar *vals = NULL;
437           PetscInt     numVals, va;
438 
439           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
440           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);
441           switch (numVals / Nc) {
442           case 3: /* P1 Triangle */
443           case 4: /* P1 Quadrangle */
444             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
445             break;
446           case 6: /* P2 Triangle */
447           case 8: /* P2 Quadrangle */
448             for (va = 0; va < numVals / (Nc * 2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp + numVals / (Nc * 2)]), vbound[0], vbound[1]);
449             break;
450           default:
451             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals / Nc);
452           }
453           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
454         }
455         PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
456         switch (numCoords) {
457         case 6:
458         case 12: /* Localized triangle */
459           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]));
460           break;
461         case 8:
462         case 16: /* Localized quadrilateral */
463           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]));
464           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]));
465           break;
466         default:
467           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
468         }
469         PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
470       }
471       PetscCall(VecRestoreArrayRead(fv, &array));
472       PetscCall(PetscDrawFlush(draw));
473       PetscCall(PetscDrawPause(draw));
474       PetscCall(PetscDrawSave(draw));
475     }
476     if (Nf > 1) {
477       PetscCall(VecRestoreSubVector(v, fis, &fv));
478       PetscCall(ISDestroy(&fis));
479       PetscCall(DMDestroy(&fdm));
480     }
481   }
482   PetscFunctionReturn(PETSC_SUCCESS);
483 }
484 
485 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
486 {
487   DM        dm;
488   PetscDraw draw;
489   PetscInt  dim;
490   PetscBool isnull;
491 
492   PetscFunctionBegin;
493   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
494   PetscCall(PetscDrawIsNull(draw, &isnull));
495   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
496 
497   PetscCall(VecGetDM(v, &dm));
498   PetscCall(DMGetCoordinateDim(dm, &dim));
499   switch (dim) {
500   case 1:
501     PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));
502     break;
503   case 2:
504     PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));
505     break;
506   default:
507     SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
508   }
509   PetscFunctionReturn(PETSC_SUCCESS);
510 }
511 
512 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
513 {
514   DM                      dm;
515   Vec                     locv;
516   const char             *name;
517   PetscSection            section;
518   PetscInt                pStart, pEnd;
519   PetscInt                numFields;
520   PetscViewerVTKFieldType ft;
521 
522   PetscFunctionBegin;
523   PetscCall(VecGetDM(v, &dm));
524   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
525   PetscCall(PetscObjectGetName((PetscObject)v, &name));
526   PetscCall(PetscObjectSetName((PetscObject)locv, name));
527   PetscCall(VecCopy(v, locv));
528   PetscCall(DMGetLocalSection(dm, &section));
529   PetscCall(PetscSectionGetNumFields(section, &numFields));
530   if (!numFields) {
531     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
532     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE, (PetscObject)locv));
533   } else {
534     PetscInt f;
535 
536     for (f = 0; f < numFields; f++) {
537       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
538       if (ft == PETSC_VTK_INVALID) continue;
539       PetscCall(PetscObjectReference((PetscObject)locv));
540       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE, (PetscObject)locv));
541     }
542     PetscCall(VecDestroy(&locv));
543   }
544   PetscFunctionReturn(PETSC_SUCCESS);
545 }
546 
547 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
548 {
549   DM        dm;
550   PetscBool isvtk, ishdf5, isdraw, isglvis, iscgns;
551 
552   PetscFunctionBegin;
553   PetscCall(VecGetDM(v, &dm));
554   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
555   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
556   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
557   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
558   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
559   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
560   if (isvtk || ishdf5 || isdraw || isglvis || iscgns) {
561     PetscInt    i, numFields;
562     PetscObject fe;
563     PetscBool   fem  = PETSC_FALSE;
564     Vec         locv = v;
565     const char *name;
566     PetscInt    step;
567     PetscReal   time;
568 
569     PetscCall(DMGetNumFields(dm, &numFields));
570     for (i = 0; i < numFields; i++) {
571       PetscCall(DMGetField(dm, i, NULL, &fe));
572       if (fe->classid == PETSCFE_CLASSID) {
573         fem = PETSC_TRUE;
574         break;
575       }
576     }
577     if (fem) {
578       PetscObject isZero;
579 
580       PetscCall(DMGetLocalVector(dm, &locv));
581       PetscCall(PetscObjectGetName((PetscObject)v, &name));
582       PetscCall(PetscObjectSetName((PetscObject)locv, name));
583       PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
584       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
585       PetscCall(VecCopy(v, locv));
586       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
587       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
588     }
589     if (isvtk) {
590       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
591     } else if (ishdf5) {
592 #if defined(PETSC_HAVE_HDF5)
593       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
594 #else
595       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
596 #endif
597     } else if (isdraw) {
598       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
599     } else if (isglvis) {
600       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
601       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
602       PetscCall(VecView_GLVis(locv, viewer));
603     } else if (iscgns) {
604 #if defined(PETSC_HAVE_CGNS)
605       PetscCall(VecView_Plex_Local_CGNS(locv, viewer));
606 #else
607       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
608 #endif
609     }
610     if (fem) {
611       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
612       PetscCall(DMRestoreLocalVector(dm, &locv));
613     }
614   } else {
615     PetscBool isseq;
616 
617     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
618     if (isseq) PetscCall(VecView_Seq(v, viewer));
619     else PetscCall(VecView_MPI(v, viewer));
620   }
621   PetscFunctionReturn(PETSC_SUCCESS);
622 }
623 
624 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
625 {
626   DM        dm;
627   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii, iscgns;
628 
629   PetscFunctionBegin;
630   PetscCall(VecGetDM(v, &dm));
631   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
632   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
633   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
634   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
635   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
636   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
637   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
638   if (isvtk || isdraw || isglvis || iscgns) {
639     Vec         locv;
640     PetscObject isZero;
641     const char *name;
642 
643     PetscCall(DMGetLocalVector(dm, &locv));
644     PetscCall(PetscObjectGetName((PetscObject)v, &name));
645     PetscCall(PetscObjectSetName((PetscObject)locv, name));
646     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
647     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
648     PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
649     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
650     PetscCall(VecView_Plex_Local(locv, viewer));
651     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
652     PetscCall(DMRestoreLocalVector(dm, &locv));
653   } else if (ishdf5) {
654 #if defined(PETSC_HAVE_HDF5)
655     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
656 #else
657     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
658 #endif
659   } else if (isexodusii) {
660 #if defined(PETSC_HAVE_EXODUSII)
661     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
662 #else
663     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
664 #endif
665   } else {
666     PetscBool isseq;
667 
668     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
669     if (isseq) PetscCall(VecView_Seq(v, viewer));
670     else PetscCall(VecView_MPI(v, viewer));
671   }
672   PetscFunctionReturn(PETSC_SUCCESS);
673 }
674 
675 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
676 {
677   DM                dm;
678   MPI_Comm          comm;
679   PetscViewerFormat format;
680   Vec               v;
681   PetscBool         isvtk, ishdf5;
682 
683   PetscFunctionBegin;
684   PetscCall(VecGetDM(originalv, &dm));
685   PetscCall(PetscObjectGetComm((PetscObject)originalv, &comm));
686   PetscCheck(dm, comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
687   PetscCall(PetscViewerGetFormat(viewer, &format));
688   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
689   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
690   if (format == PETSC_VIEWER_NATIVE) {
691     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
692     /* this need a better fix */
693     if (dm->useNatural) {
694       if (dm->sfNatural) {
695         const char *vecname;
696         PetscInt    n, nroots;
697 
698         PetscCall(VecGetLocalSize(originalv, &n));
699         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
700         if (n == nroots) {
701           PetscCall(DMPlexCreateNaturalVector(dm, &v));
702           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
703           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
704           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
705           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
706         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
707       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
708     } else v = originalv;
709   } else v = originalv;
710 
711   if (ishdf5) {
712 #if defined(PETSC_HAVE_HDF5)
713     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
714 #else
715     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
716 #endif
717   } else if (isvtk) {
718     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
719   } else {
720     PetscBool isseq;
721 
722     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
723     if (isseq) PetscCall(VecView_Seq(v, viewer));
724     else PetscCall(VecView_MPI(v, viewer));
725   }
726   if (v != originalv) PetscCall(VecDestroy(&v));
727   PetscFunctionReturn(PETSC_SUCCESS);
728 }
729 
730 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
731 {
732   DM        dm;
733   PetscBool ishdf5;
734 
735   PetscFunctionBegin;
736   PetscCall(VecGetDM(v, &dm));
737   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
738   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
739   if (ishdf5) {
740     DM          dmBC;
741     Vec         gv;
742     const char *name;
743 
744     PetscCall(DMGetOutputDM(dm, &dmBC));
745     PetscCall(DMGetGlobalVector(dmBC, &gv));
746     PetscCall(PetscObjectGetName((PetscObject)v, &name));
747     PetscCall(PetscObjectSetName((PetscObject)gv, name));
748     PetscCall(VecLoad_Default(gv, viewer));
749     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
750     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
751     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
752   } else PetscCall(VecLoad_Default(v, viewer));
753   PetscFunctionReturn(PETSC_SUCCESS);
754 }
755 
756 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
757 {
758   DM        dm;
759   PetscBool ishdf5, isexodusii;
760 
761   PetscFunctionBegin;
762   PetscCall(VecGetDM(v, &dm));
763   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
764   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
765   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
766   if (ishdf5) {
767 #if defined(PETSC_HAVE_HDF5)
768     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
769 #else
770     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
771 #endif
772   } else if (isexodusii) {
773 #if defined(PETSC_HAVE_EXODUSII)
774     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
775 #else
776     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
777 #endif
778   } else PetscCall(VecLoad_Default(v, viewer));
779   PetscFunctionReturn(PETSC_SUCCESS);
780 }
781 
782 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
783 {
784   DM                dm;
785   PetscViewerFormat format;
786   PetscBool         ishdf5;
787 
788   PetscFunctionBegin;
789   PetscCall(VecGetDM(originalv, &dm));
790   PetscCheck(dm, PetscObjectComm((PetscObject)originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
791   PetscCall(PetscViewerGetFormat(viewer, &format));
792   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
793   if (format == PETSC_VIEWER_NATIVE) {
794     if (dm->useNatural) {
795       if (dm->sfNatural) {
796         if (ishdf5) {
797 #if defined(PETSC_HAVE_HDF5)
798           Vec         v;
799           const char *vecname;
800 
801           PetscCall(DMPlexCreateNaturalVector(dm, &v));
802           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
803           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
804           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
805           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
806           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
807           PetscCall(VecDestroy(&v));
808 #else
809           SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
810 #endif
811         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
812       }
813     } else PetscCall(VecLoad_Default(originalv, viewer));
814   }
815   PetscFunctionReturn(PETSC_SUCCESS);
816 }
817 
818 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
819 {
820   PetscSection       coordSection;
821   Vec                coordinates;
822   DMLabel            depthLabel, celltypeLabel;
823   const char        *name[4];
824   const PetscScalar *a;
825   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
826 
827   PetscFunctionBegin;
828   PetscCall(DMGetDimension(dm, &dim));
829   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
830   PetscCall(DMGetCoordinateSection(dm, &coordSection));
831   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
832   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
833   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
834   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
835   PetscCall(VecGetArrayRead(coordinates, &a));
836   name[0]       = "vertex";
837   name[1]       = "edge";
838   name[dim - 1] = "face";
839   name[dim]     = "cell";
840   for (c = cStart; c < cEnd; ++c) {
841     PetscInt *closure = NULL;
842     PetscInt  closureSize, cl, ct;
843 
844     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
845     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
846     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
847     PetscCall(PetscViewerASCIIPushTab(viewer));
848     for (cl = 0; cl < closureSize * 2; cl += 2) {
849       PetscInt point = closure[cl], depth, dof, off, d, p;
850 
851       if ((point < pStart) || (point >= pEnd)) continue;
852       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
853       if (!dof) continue;
854       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
855       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
856       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
857       for (p = 0; p < dof / dim; ++p) {
858         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
859         for (d = 0; d < dim; ++d) {
860           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
861           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double)PetscRealPart(a[off + p * dim + d])));
862         }
863         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
864       }
865       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
866     }
867     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
868     PetscCall(PetscViewerASCIIPopTab(viewer));
869   }
870   PetscCall(VecRestoreArrayRead(coordinates, &a));
871   PetscFunctionReturn(PETSC_SUCCESS);
872 }
873 
874 typedef enum {
875   CS_CARTESIAN,
876   CS_POLAR,
877   CS_CYLINDRICAL,
878   CS_SPHERICAL
879 } CoordSystem;
880 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
881 
882 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
883 {
884   PetscInt i;
885 
886   PetscFunctionBegin;
887   if (dim > 3) {
888     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)PetscRealPart(x[i])));
889   } else {
890     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
891 
892     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
893     switch (cs) {
894     case CS_CARTESIAN:
895       for (i = 0; i < dim; ++i) trcoords[i] = coords[i];
896       break;
897     case CS_POLAR:
898       PetscCheck(dim == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
899       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
900       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
901       break;
902     case CS_CYLINDRICAL:
903       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
904       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
905       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
906       trcoords[2] = coords[2];
907       break;
908     case CS_SPHERICAL:
909       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
910       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
911       trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
912       trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
913       break;
914     }
915     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)trcoords[i]));
916   }
917   PetscFunctionReturn(PETSC_SUCCESS);
918 }
919 
920 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
921 {
922   DM_Plex          *mesh = (DM_Plex *)dm->data;
923   DM                cdm, cdmCell;
924   PetscSection      coordSection, coordSectionCell;
925   Vec               coordinates, coordinatesCell;
926   PetscViewerFormat format;
927 
928   PetscFunctionBegin;
929   PetscCall(PetscViewerGetFormat(viewer, &format));
930   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
931     const char *name;
932     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
933     PetscInt    pStart, pEnd, p, numLabels, l;
934     PetscMPIInt rank, size;
935 
936     PetscCall(DMGetCoordinateDM(dm, &cdm));
937     PetscCall(DMGetCoordinateSection(dm, &coordSection));
938     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
939     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
940     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
941     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
942     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
943     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
944     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
945     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
946     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
947     PetscCall(DMGetDimension(dm, &dim));
948     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
949     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
950     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
951     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
952     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
953     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
954     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
955     for (p = pStart; p < pEnd; ++p) {
956       PetscInt dof, off, s;
957 
958       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
959       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
960       for (s = off; s < off + dof; ++s) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
961     }
962     PetscCall(PetscViewerFlush(viewer));
963     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
964     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
965     for (p = pStart; p < pEnd; ++p) {
966       PetscInt dof, off, c;
967 
968       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
969       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
970       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]));
971     }
972     PetscCall(PetscViewerFlush(viewer));
973     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
974     if (coordSection && coordinates) {
975       CoordSystem        cs = CS_CARTESIAN;
976       const PetscScalar *array, *arrayCell = NULL;
977       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_MAX_INT, pcEnd = PETSC_MIN_INT, pStart, pEnd, p;
978       PetscMPIInt        rank;
979       const char        *name;
980 
981       PetscCall(PetscOptionsGetEnum(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *)&cs, NULL));
982       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
983       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
984       PetscCheck(Nf == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
985       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
986       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
987       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
988       pStart = PetscMin(pvStart, pcStart);
989       pEnd   = PetscMax(pvEnd, pcEnd);
990       PetscCall(PetscObjectGetName((PetscObject)coordinates, &name));
991       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
992       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
993       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
994 
995       PetscCall(VecGetArrayRead(coordinates, &array));
996       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
997       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
998       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
999       for (p = pStart; p < pEnd; ++p) {
1000         PetscInt dof, off;
1001 
1002         if (p >= pvStart && p < pvEnd) {
1003           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
1004           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
1005           if (dof) {
1006             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1007             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
1008             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1009           }
1010         }
1011         if (cdmCell && p >= pcStart && p < pcEnd) {
1012           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
1013           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
1014           if (dof) {
1015             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1016             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
1017             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1018           }
1019         }
1020       }
1021       PetscCall(PetscViewerFlush(viewer));
1022       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1023       PetscCall(VecRestoreArrayRead(coordinates, &array));
1024       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
1025     }
1026     PetscCall(DMGetNumLabels(dm, &numLabels));
1027     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1028     for (l = 0; l < numLabels; ++l) {
1029       DMLabel     label;
1030       PetscBool   isdepth;
1031       const char *name;
1032 
1033       PetscCall(DMGetLabelName(dm, l, &name));
1034       PetscCall(PetscStrcmp(name, "depth", &isdepth));
1035       if (isdepth) continue;
1036       PetscCall(DMGetLabel(dm, name, &label));
1037       PetscCall(DMLabelView(label, viewer));
1038     }
1039     if (size > 1) {
1040       PetscSF sf;
1041 
1042       PetscCall(DMGetPointSF(dm, &sf));
1043       PetscCall(PetscSFView(sf, viewer));
1044     }
1045     if (mesh->periodic.face_sf) PetscCall(PetscSFView(mesh->periodic.face_sf, viewer));
1046     PetscCall(PetscViewerFlush(viewer));
1047   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
1048     const char  *name, *color;
1049     const char  *defcolors[3]  = {"gray", "orange", "green"};
1050     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
1051     char         lname[PETSC_MAX_PATH_LEN];
1052     PetscReal    scale      = 2.0;
1053     PetscReal    tikzscale  = 1.0;
1054     PetscBool    useNumbers = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
1055     double       tcoords[3];
1056     PetscScalar *coords;
1057     PetscInt     numLabels, l, numColors, numLColors, dim, d, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, e, p, n;
1058     PetscMPIInt  rank, size;
1059     char       **names, **colors, **lcolors;
1060     PetscBool    flg, lflg;
1061     PetscBT      wp = NULL;
1062     PetscInt     pEnd, pStart;
1063 
1064     PetscCall(DMGetCoordinateDM(dm, &cdm));
1065     PetscCall(DMGetCoordinateSection(dm, &coordSection));
1066     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1067     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
1068     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
1069     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
1070     PetscCall(DMGetDimension(dm, &dim));
1071     PetscCall(DMPlexGetDepth(dm, &depth));
1072     PetscCall(DMGetNumLabels(dm, &numLabels));
1073     numLabels  = PetscMax(numLabels, 10);
1074     numColors  = 10;
1075     numLColors = 10;
1076     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
1077     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
1078     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
1079     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
1080     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
1081     for (d = 0; d < 4; ++d) drawColors[d] = PETSC_TRUE;
1082     n = 4;
1083     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
1084     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1085     n = 4;
1086     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
1087     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1088     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
1089     if (!useLabels) numLabels = 0;
1090     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
1091     if (!useColors) {
1092       numColors = 3;
1093       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
1094     }
1095     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
1096     if (!useColors) {
1097       numLColors = 4;
1098       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
1099     }
1100     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
1101     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
1102     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
1103     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
1104     if (depth < dim) plotEdges = PETSC_FALSE;
1105     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
1106 
1107     /* filter points with labelvalue != labeldefaultvalue */
1108     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1109     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1110     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
1111     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1112     if (lflg) {
1113       DMLabel lbl;
1114 
1115       PetscCall(DMGetLabel(dm, lname, &lbl));
1116       if (lbl) {
1117         PetscInt val, defval;
1118 
1119         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
1120         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
1121         for (c = pStart; c < pEnd; c++) {
1122           PetscInt *closure = NULL;
1123           PetscInt  closureSize;
1124 
1125           PetscCall(DMLabelGetValue(lbl, c, &val));
1126           if (val == defval) continue;
1127 
1128           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1129           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
1130           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1131         }
1132       }
1133     }
1134 
1135     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1136     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1137     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1138     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1139 \\documentclass[tikz]{standalone}\n\n\
1140 \\usepackage{pgflibraryshapes}\n\
1141 \\usetikzlibrary{backgrounds}\n\
1142 \\usetikzlibrary{arrows}\n\
1143 \\begin{document}\n"));
1144     if (size > 1) {
1145       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1146       for (p = 0; p < size; ++p) {
1147         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1148         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1149       }
1150       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1151     }
1152     if (drawHasse) {
1153       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, cEnd - cStart));
1154 
1155       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1156       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1157       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1158       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1159       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1160       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1161       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1162       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1163       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1164       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1165       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1166       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1167     }
1168     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1169 
1170     /* Plot vertices */
1171     PetscCall(VecGetArray(coordinates, &coords));
1172     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1173     for (v = vStart; v < vEnd; ++v) {
1174       PetscInt  off, dof, d;
1175       PetscBool isLabeled = PETSC_FALSE;
1176 
1177       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1178       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1179       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1180       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1181       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1182       for (d = 0; d < dof; ++d) {
1183         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1184         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1185       }
1186       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1187       if (dim == 3) {
1188         PetscReal tmp = tcoords[1];
1189         tcoords[1]    = tcoords[2];
1190         tcoords[2]    = -tmp;
1191       }
1192       for (d = 0; d < dof; ++d) {
1193         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1194         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1195       }
1196       if (drawHasse) color = colors[0 % numColors];
1197       else color = colors[rank % numColors];
1198       for (l = 0; l < numLabels; ++l) {
1199         PetscInt val;
1200         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1201         if (val >= 0) {
1202           color     = lcolors[l % numLColors];
1203           isLabeled = PETSC_TRUE;
1204           break;
1205         }
1206       }
1207       if (drawNumbers[0]) {
1208         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1209       } else if (drawColors[0]) {
1210         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1211       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1212     }
1213     PetscCall(VecRestoreArray(coordinates, &coords));
1214     PetscCall(PetscViewerFlush(viewer));
1215     /* Plot edges */
1216     if (plotEdges) {
1217       PetscCall(VecGetArray(coordinates, &coords));
1218       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1219       for (e = eStart; e < eEnd; ++e) {
1220         const PetscInt *cone;
1221         PetscInt        coneSize, offA, offB, dof, d;
1222 
1223         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1224         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1225         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1226         PetscCall(DMPlexGetCone(dm, e, &cone));
1227         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1228         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1229         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1230         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1231         for (d = 0; d < dof; ++d) {
1232           tcoords[d] = (double)(0.5 * scale * PetscRealPart(coords[offA + d] + coords[offB + d]));
1233           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1234         }
1235         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1236         if (dim == 3) {
1237           PetscReal tmp = tcoords[1];
1238           tcoords[1]    = tcoords[2];
1239           tcoords[2]    = -tmp;
1240         }
1241         for (d = 0; d < dof; ++d) {
1242           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1243           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1244         }
1245         if (drawHasse) color = colors[1 % numColors];
1246         else color = colors[rank % numColors];
1247         for (l = 0; l < numLabels; ++l) {
1248           PetscInt val;
1249           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1250           if (val >= 0) {
1251             color = lcolors[l % numLColors];
1252             break;
1253           }
1254         }
1255         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1256       }
1257       PetscCall(VecRestoreArray(coordinates, &coords));
1258       PetscCall(PetscViewerFlush(viewer));
1259       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1260     }
1261     /* Plot cells */
1262     if (dim == 3 || !drawNumbers[1]) {
1263       for (e = eStart; e < eEnd; ++e) {
1264         const PetscInt *cone;
1265 
1266         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1267         color = colors[rank % numColors];
1268         for (l = 0; l < numLabels; ++l) {
1269           PetscInt val;
1270           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1271           if (val >= 0) {
1272             color = lcolors[l % numLColors];
1273             break;
1274           }
1275         }
1276         PetscCall(DMPlexGetCone(dm, e, &cone));
1277         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1278       }
1279     } else {
1280       DMPolytopeType ct;
1281 
1282       /* Drawing a 2D polygon */
1283       for (c = cStart; c < cEnd; ++c) {
1284         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1285         PetscCall(DMPlexGetCellType(dm, c, &ct));
1286         if (DMPolytopeTypeIsHybrid(ct)) {
1287           const PetscInt *cone;
1288           PetscInt        coneSize, e;
1289 
1290           PetscCall(DMPlexGetCone(dm, c, &cone));
1291           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1292           for (e = 0; e < coneSize; ++e) {
1293             const PetscInt *econe;
1294 
1295             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1296             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));
1297           }
1298         } else {
1299           PetscInt *closure = NULL;
1300           PetscInt  closureSize, Nv = 0, v;
1301 
1302           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1303           for (p = 0; p < closureSize * 2; p += 2) {
1304             const PetscInt point = closure[p];
1305 
1306             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1307           }
1308           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1309           for (v = 0; v <= Nv; ++v) {
1310             const PetscInt vertex = closure[v % Nv];
1311 
1312             if (v > 0) {
1313               if (plotEdges) {
1314                 const PetscInt *edge;
1315                 PetscInt        endpoints[2], ne;
1316 
1317                 endpoints[0] = closure[v - 1];
1318                 endpoints[1] = vertex;
1319                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1320                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1321                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1322                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1323               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1324             }
1325             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1326           }
1327           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1328           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1329         }
1330       }
1331     }
1332     for (c = cStart; c < cEnd; ++c) {
1333       double             ccoords[3] = {0.0, 0.0, 0.0};
1334       PetscBool          isLabeled  = PETSC_FALSE;
1335       PetscScalar       *cellCoords = NULL;
1336       const PetscScalar *array;
1337       PetscInt           numCoords, cdim, d;
1338       PetscBool          isDG;
1339 
1340       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1341       PetscCall(DMGetCoordinateDim(dm, &cdim));
1342       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1343       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1344       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1345       for (p = 0; p < numCoords / cdim; ++p) {
1346         for (d = 0; d < cdim; ++d) {
1347           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1348           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1349         }
1350         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1351         if (cdim == 3) {
1352           PetscReal tmp = tcoords[1];
1353           tcoords[1]    = tcoords[2];
1354           tcoords[2]    = -tmp;
1355         }
1356         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1357       }
1358       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1359       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1360       for (d = 0; d < cdim; ++d) {
1361         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1362         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)ccoords[d]));
1363       }
1364       if (drawHasse) color = colors[depth % numColors];
1365       else color = colors[rank % numColors];
1366       for (l = 0; l < numLabels; ++l) {
1367         PetscInt val;
1368         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1369         if (val >= 0) {
1370           color     = lcolors[l % numLColors];
1371           isLabeled = PETSC_TRUE;
1372           break;
1373         }
1374       }
1375       if (drawNumbers[dim]) {
1376         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1377       } else if (drawColors[dim]) {
1378         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1379       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1380     }
1381     if (drawHasse) {
1382       color = colors[depth % numColors];
1383       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1384       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1385       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1386       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,0) {\\c};\n", rank, color));
1387       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1388 
1389       color = colors[1 % numColors];
1390       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1391       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1392       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1393       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,1) {\\e};\n", rank, color));
1394       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1395 
1396       color = colors[0 % numColors];
1397       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1398       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1399       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1400       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,2) {\\v};\n", rank, color));
1401       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1402 
1403       for (p = pStart; p < pEnd; ++p) {
1404         const PetscInt *cone;
1405         PetscInt        coneSize, cp;
1406 
1407         PetscCall(DMPlexGetCone(dm, p, &cone));
1408         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1409         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1410       }
1411     }
1412     PetscCall(PetscViewerFlush(viewer));
1413     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1414     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1415     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1416     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1417     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1418     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1419     PetscCall(PetscFree3(names, colors, lcolors));
1420     PetscCall(PetscBTDestroy(&wp));
1421   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1422     Vec                    cown, acown;
1423     VecScatter             sct;
1424     ISLocalToGlobalMapping g2l;
1425     IS                     gid, acis;
1426     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1427     MPI_Group              ggroup, ngroup;
1428     PetscScalar           *array, nid;
1429     const PetscInt        *idxs;
1430     PetscInt              *idxs2, *start, *adjacency, *work;
1431     PetscInt64             lm[3], gm[3];
1432     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1433     PetscMPIInt            d1, d2, rank;
1434 
1435     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1436     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1437 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1438     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1439 #endif
1440     if (ncomm != MPI_COMM_NULL) {
1441       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1442       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1443       d1 = 0;
1444       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1445       nid = d2;
1446       PetscCallMPI(MPI_Group_free(&ggroup));
1447       PetscCallMPI(MPI_Group_free(&ngroup));
1448       PetscCallMPI(MPI_Comm_free(&ncomm));
1449     } else nid = 0.0;
1450 
1451     /* Get connectivity */
1452     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1453     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1454 
1455     /* filter overlapped local cells */
1456     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1457     PetscCall(ISGetIndices(gid, &idxs));
1458     PetscCall(ISGetLocalSize(gid, &cum));
1459     PetscCall(PetscMalloc1(cum, &idxs2));
1460     for (c = cStart, cum = 0; c < cEnd; c++) {
1461       if (idxs[c - cStart] < 0) continue;
1462       idxs2[cum++] = idxs[c - cStart];
1463     }
1464     PetscCall(ISRestoreIndices(gid, &idxs));
1465     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1466     PetscCall(ISDestroy(&gid));
1467     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1468 
1469     /* support for node-aware cell locality */
1470     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1471     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1472     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1473     PetscCall(VecGetArray(cown, &array));
1474     for (c = 0; c < numVertices; c++) array[c] = nid;
1475     PetscCall(VecRestoreArray(cown, &array));
1476     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1477     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1478     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1479     PetscCall(ISDestroy(&acis));
1480     PetscCall(VecScatterDestroy(&sct));
1481     PetscCall(VecDestroy(&cown));
1482 
1483     /* compute edgeCut */
1484     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1485     PetscCall(PetscMalloc1(cum, &work));
1486     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1487     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1488     PetscCall(ISDestroy(&gid));
1489     PetscCall(VecGetArray(acown, &array));
1490     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1491       PetscInt totl;
1492 
1493       totl = start[c + 1] - start[c];
1494       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1495       for (i = 0; i < totl; i++) {
1496         if (work[i] < 0) {
1497           ect += 1;
1498           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1499         }
1500       }
1501     }
1502     PetscCall(PetscFree(work));
1503     PetscCall(VecRestoreArray(acown, &array));
1504     lm[0] = numVertices > 0 ? numVertices : PETSC_MAX_INT;
1505     lm[1] = -numVertices;
1506     PetscCall(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1507     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT, -((double)gm[1]) / ((double)gm[0]), -(PetscInt)gm[1], (PetscInt)gm[0]));
1508     lm[0] = ect;                     /* edgeCut */
1509     lm[1] = ectn;                    /* node-aware edgeCut */
1510     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1511     PetscCall(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1512     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt_FMT ")\n", (PetscInt)gm[2]));
1513 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1514     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), gm[0] ? ((double)gm[1]) / ((double)gm[0]) : 1.));
1515 #else
1516     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), 0.0));
1517 #endif
1518     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1519     PetscCall(PetscFree(start));
1520     PetscCall(PetscFree(adjacency));
1521     PetscCall(VecDestroy(&acown));
1522   } else {
1523     const char    *name;
1524     PetscInt      *sizes, *hybsizes, *ghostsizes;
1525     PetscInt       locDepth, depth, cellHeight, dim, d;
1526     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1527     PetscInt       numLabels, l, maxSize = 17;
1528     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1529     MPI_Comm       comm;
1530     PetscMPIInt    size, rank;
1531 
1532     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1533     PetscCallMPI(MPI_Comm_size(comm, &size));
1534     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1535     PetscCall(DMGetDimension(dm, &dim));
1536     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1537     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1538     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1539     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1540     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1541     PetscCall(DMPlexGetDepth(dm, &locDepth));
1542     PetscCall(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1543     PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &gcStart, &gcEnd));
1544     gcNum = gcEnd - gcStart;
1545     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1546     else PetscCall(PetscCalloc3(3, &sizes, 3, &hybsizes, 3, &ghostsizes));
1547     for (d = 0; d <= depth; d++) {
1548       PetscInt Nc[2] = {0, 0}, ict;
1549 
1550       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1551       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1552       ict = ct0;
1553       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1554       ct0 = (DMPolytopeType)ict;
1555       for (p = pStart; p < pEnd; ++p) {
1556         DMPolytopeType ct;
1557 
1558         PetscCall(DMPlexGetCellType(dm, p, &ct));
1559         if (ct == ct0) ++Nc[0];
1560         else ++Nc[1];
1561       }
1562       if (size < maxSize) {
1563         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes, 1, MPIU_INT, 0, comm));
1564         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1565         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1566         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1567         for (p = 0; p < size; ++p) {
1568           if (rank == 0) {
1569             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p] + hybsizes[p]));
1570             if (hybsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1571             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1572           }
1573         }
1574       } else {
1575         PetscInt locMinMax[2];
1576 
1577         locMinMax[0] = Nc[0] + Nc[1];
1578         locMinMax[1] = Nc[0] + Nc[1];
1579         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1580         locMinMax[0] = Nc[1];
1581         locMinMax[1] = Nc[1];
1582         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1583         if (d == depth) {
1584           locMinMax[0] = gcNum;
1585           locMinMax[1] = gcNum;
1586           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1587         }
1588         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1589         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1590         if (hybsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1591         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1592       }
1593       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1594     }
1595     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1596     {
1597       const PetscReal *maxCell;
1598       const PetscReal *L;
1599       PetscBool        localized;
1600 
1601       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1602       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1603       if (L || localized) {
1604         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1605         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1606         if (L) {
1607           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1608           for (d = 0; d < dim; ++d) {
1609             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1610             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1611           }
1612           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1613         }
1614         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1615         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1616       }
1617     }
1618     PetscCall(DMGetNumLabels(dm, &numLabels));
1619     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1620     for (l = 0; l < numLabels; ++l) {
1621       DMLabel         label;
1622       const char     *name;
1623       IS              valueIS;
1624       const PetscInt *values;
1625       PetscInt        numValues, v;
1626 
1627       PetscCall(DMGetLabelName(dm, l, &name));
1628       PetscCall(DMGetLabel(dm, name, &label));
1629       PetscCall(DMLabelGetNumValues(label, &numValues));
1630       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1631       PetscCall(DMLabelGetValueIS(label, &valueIS));
1632       PetscCall(ISGetIndices(valueIS, &values));
1633       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1634       for (v = 0; v < numValues; ++v) {
1635         PetscInt size;
1636 
1637         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1638         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1639         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1640       }
1641       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1642       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1643       PetscCall(ISRestoreIndices(valueIS, &values));
1644       PetscCall(ISDestroy(&valueIS));
1645     }
1646     {
1647       char    **labelNames;
1648       PetscInt  Nl = numLabels;
1649       PetscBool flg;
1650 
1651       PetscCall(PetscMalloc1(Nl, &labelNames));
1652       PetscCall(PetscOptionsGetStringArray(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1653       for (l = 0; l < Nl; ++l) {
1654         DMLabel label;
1655 
1656         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1657         if (flg) {
1658           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1659           PetscCall(DMLabelView(label, viewer));
1660         }
1661         PetscCall(PetscFree(labelNames[l]));
1662       }
1663       PetscCall(PetscFree(labelNames));
1664     }
1665     /* If no fields are specified, people do not want to see adjacency */
1666     if (dm->Nf) {
1667       PetscInt f;
1668 
1669       for (f = 0; f < dm->Nf; ++f) {
1670         const char *name;
1671 
1672         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1673         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1674         PetscCall(PetscViewerASCIIPushTab(viewer));
1675         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1676         if (dm->fields[f].adjacency[0]) {
1677           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1678           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1679         } else {
1680           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1681           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1682         }
1683         PetscCall(PetscViewerASCIIPopTab(viewer));
1684       }
1685     }
1686     PetscCall(DMGetCoarseDM(dm, &cdm));
1687     if (cdm) {
1688       PetscCall(PetscViewerASCIIPushTab(viewer));
1689       PetscCall(PetscViewerASCIIPrintf(viewer, "Defined by transform from:\n"));
1690       PetscCall(DMPlexView_Ascii(cdm, viewer));
1691       PetscCall(PetscViewerASCIIPopTab(viewer));
1692     }
1693   }
1694   PetscFunctionReturn(PETSC_SUCCESS);
1695 }
1696 
1697 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1698 {
1699   DMPolytopeType ct;
1700   PetscMPIInt    rank;
1701   PetscInt       cdim;
1702 
1703   PetscFunctionBegin;
1704   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1705   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1706   PetscCall(DMGetCoordinateDim(dm, &cdim));
1707   switch (ct) {
1708   case DM_POLYTOPE_SEGMENT:
1709   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1710     switch (cdim) {
1711     case 1: {
1712       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1713       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1714 
1715       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, PETSC_DRAW_BLACK));
1716       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, PETSC_DRAW_BLACK));
1717       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, PETSC_DRAW_BLACK));
1718     } break;
1719     case 2: {
1720       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1721       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1722       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1723 
1724       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1725       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));
1726       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));
1727     } break;
1728     default:
1729       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1730     }
1731     break;
1732   case DM_POLYTOPE_TRIANGLE:
1733     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));
1734     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1735     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1736     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1737     break;
1738   case DM_POLYTOPE_QUADRILATERAL:
1739     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));
1740     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));
1741     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1742     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1743     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1744     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1745     break;
1746   case DM_POLYTOPE_SEG_PRISM_TENSOR:
1747     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));
1748     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));
1749     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1750     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1751     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1752     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1753     break;
1754   case DM_POLYTOPE_FV_GHOST:
1755     break;
1756   default:
1757     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1758   }
1759   PetscFunctionReturn(PETSC_SUCCESS);
1760 }
1761 
1762 static PetscErrorCode DrawPolygon_Private(DM dm, PetscDraw draw, PetscInt cell, PetscInt Nv, const PetscReal refVertices[], const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1763 {
1764   PetscReal   centroid[2] = {0., 0.};
1765   PetscMPIInt rank;
1766   PetscInt    fillColor;
1767 
1768   PetscFunctionBegin;
1769   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1770   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1771   for (PetscInt v = 0; v < Nv; ++v) {
1772     centroid[0] += PetscRealPart(coords[v * 2 + 0]) / Nv;
1773     centroid[1] += PetscRealPart(coords[v * 2 + 1]) / Nv;
1774   }
1775   for (PetscInt e = 0; e < Nv; ++e) {
1776     refCoords[0] = refVertices[e * 2 + 0];
1777     refCoords[1] = refVertices[e * 2 + 1];
1778     for (PetscInt d = 1; d <= edgeDiv; ++d) {
1779       refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % Nv * 2 + 0] - refCoords[0]) * d / edgeDiv;
1780       refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % Nv * 2 + 1] - refCoords[1]) * d / edgeDiv;
1781     }
1782     PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1783     for (PetscInt d = 0; d < edgeDiv; ++d) {
1784       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));
1785       PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1786     }
1787   }
1788   PetscFunctionReturn(PETSC_SUCCESS);
1789 }
1790 
1791 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1792 {
1793   DMPolytopeType ct;
1794 
1795   PetscFunctionBegin;
1796   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1797   switch (ct) {
1798   case DM_POLYTOPE_TRIANGLE: {
1799     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1800 
1801     PetscCall(DrawPolygon_Private(dm, draw, cell, 3, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1802   } break;
1803   case DM_POLYTOPE_QUADRILATERAL: {
1804     PetscReal refVertices[8] = {-1., -1., 1., -1., 1., 1., -1., 1.};
1805 
1806     PetscCall(DrawPolygon_Private(dm, draw, cell, 4, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1807   } break;
1808   default:
1809     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1810   }
1811   PetscFunctionReturn(PETSC_SUCCESS);
1812 }
1813 
1814 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1815 {
1816   PetscDraw    draw;
1817   DM           cdm;
1818   PetscSection coordSection;
1819   Vec          coordinates;
1820   PetscReal    xyl[3], xyr[3];
1821   PetscReal   *refCoords, *edgeCoords;
1822   PetscBool    isnull, drawAffine;
1823   PetscInt     dim, vStart, vEnd, cStart, cEnd, c, cDegree, edgeDiv;
1824 
1825   PetscFunctionBegin;
1826   PetscCall(DMGetCoordinateDim(dm, &dim));
1827   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1828   PetscCall(DMGetCoordinateDegree_Internal(dm, &cDegree));
1829   drawAffine = cDegree > 1 ? PETSC_FALSE : PETSC_TRUE;
1830   edgeDiv    = cDegree + 1;
1831   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1832   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1833   PetscCall(DMGetCoordinateDM(dm, &cdm));
1834   PetscCall(DMGetLocalSection(cdm, &coordSection));
1835   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1836   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1837   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1838 
1839   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1840   PetscCall(PetscDrawIsNull(draw, &isnull));
1841   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
1842   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1843 
1844   PetscCall(DMGetBoundingBox(dm, xyl, xyr));
1845   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1846   PetscCall(PetscDrawClear(draw));
1847 
1848   for (c = cStart; c < cEnd; ++c) {
1849     PetscScalar       *coords = NULL;
1850     const PetscScalar *coords_arr;
1851     PetscInt           numCoords;
1852     PetscBool          isDG;
1853 
1854     PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1855     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1856     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1857     PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1858   }
1859   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1860   PetscCall(PetscDrawFlush(draw));
1861   PetscCall(PetscDrawPause(draw));
1862   PetscCall(PetscDrawSave(draw));
1863   PetscFunctionReturn(PETSC_SUCCESS);
1864 }
1865 
1866 static PetscErrorCode DMPlexCreateHighOrderSurrogate_Internal(DM dm, DM *hdm)
1867 {
1868   DM           odm = dm, rdm = dm, cdm;
1869   PetscFE      fe;
1870   PetscSpace   sp;
1871   PetscClassId id;
1872   PetscInt     degree;
1873   PetscBool    hoView = PETSC_TRUE;
1874 
1875   PetscFunctionBegin;
1876   PetscObjectOptionsBegin((PetscObject)dm);
1877   PetscCall(PetscOptionsBool("-dm_plex_high_order_view", "Subsample to view meshes with high order coordinates", "DMPlexCreateHighOrderSurrogate_Internal", hoView, &hoView, NULL));
1878   PetscOptionsEnd();
1879   PetscCall(PetscObjectReference((PetscObject)dm));
1880   *hdm = dm;
1881   if (!hoView) PetscFunctionReturn(PETSC_SUCCESS);
1882   PetscCall(DMGetCoordinateDM(dm, &cdm));
1883   PetscCall(DMGetField(cdm, 0, NULL, (PetscObject *)&fe));
1884   PetscCall(PetscObjectGetClassId((PetscObject)fe, &id));
1885   if (id != PETSCFE_CLASSID) PetscFunctionReturn(PETSC_SUCCESS);
1886   PetscCall(PetscFEGetBasisSpace(fe, &sp));
1887   PetscCall(PetscSpaceGetDegree(sp, &degree, NULL));
1888   for (PetscInt r = 0, rd = PetscCeilReal(((PetscReal)degree) / 2.); r < (PetscInt)PetscCeilReal(PetscLog2Real(degree)); ++r, rd = PetscCeilReal(((PetscReal)rd) / 2.)) {
1889     DM  cdm, rcdm;
1890     Mat In;
1891     Vec cl, rcl;
1892 
1893     PetscCall(DMRefine(odm, PetscObjectComm((PetscObject)odm), &rdm));
1894     PetscCall(DMPlexCreateCoordinateSpace(rdm, rd, PETSC_FALSE, NULL));
1895     PetscCall(PetscObjectSetName((PetscObject)rdm, "Refined Mesh with Linear Coordinates"));
1896     PetscCall(DMGetCoordinateDM(odm, &cdm));
1897     PetscCall(DMGetCoordinateDM(rdm, &rcdm));
1898     PetscCall(DMGetCoordinatesLocal(odm, &cl));
1899     PetscCall(DMGetCoordinatesLocal(rdm, &rcl));
1900     PetscCall(DMSetCoarseDM(rcdm, cdm));
1901     PetscCall(DMCreateInterpolation(cdm, rcdm, &In, NULL));
1902     PetscCall(MatMult(In, cl, rcl));
1903     PetscCall(MatDestroy(&In));
1904     PetscCall(DMSetCoordinatesLocal(rdm, rcl));
1905     PetscCall(DMDestroy(&odm));
1906     odm = rdm;
1907   }
1908   *hdm = rdm;
1909   PetscFunctionReturn(PETSC_SUCCESS);
1910 }
1911 
1912 #if defined(PETSC_HAVE_EXODUSII)
1913   #include <exodusII.h>
1914   #include <petscviewerexodusii.h>
1915 #endif
1916 
1917 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1918 {
1919   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns;
1920   char      name[PETSC_MAX_PATH_LEN];
1921 
1922   PetscFunctionBegin;
1923   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1924   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1925   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
1926   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
1927   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1928   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
1929   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
1930   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
1931   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
1932   if (iascii) {
1933     PetscViewerFormat format;
1934     PetscCall(PetscViewerGetFormat(viewer, &format));
1935     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1936     else PetscCall(DMPlexView_Ascii(dm, viewer));
1937   } else if (ishdf5) {
1938 #if defined(PETSC_HAVE_HDF5)
1939     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1940 #else
1941     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1942 #endif
1943   } else if (isvtk) {
1944     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
1945   } else if (isdraw) {
1946     DM hdm;
1947 
1948     PetscCall(DMPlexCreateHighOrderSurrogate_Internal(dm, &hdm));
1949     PetscCall(DMPlexView_Draw(hdm, viewer));
1950     PetscCall(DMDestroy(&hdm));
1951   } else if (isglvis) {
1952     PetscCall(DMPlexView_GLVis(dm, viewer));
1953 #if defined(PETSC_HAVE_EXODUSII)
1954   } else if (isexodus) {
1955     /*
1956       exodusII requires that all sets be part of exactly one cell set.
1957       If the dm does not have a "Cell Sets" label defined, we create one
1958       with ID 1, containing all cells.
1959       Note that if the Cell Sets label is defined but does not cover all cells,
1960       we may still have a problem. This should probably be checked here or in the viewer;
1961     */
1962     PetscInt numCS;
1963     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
1964     if (!numCS) {
1965       PetscInt cStart, cEnd, c;
1966       PetscCall(DMCreateLabel(dm, "Cell Sets"));
1967       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1968       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
1969     }
1970     PetscCall(DMView_PlexExodusII(dm, viewer));
1971 #endif
1972 #if defined(PETSC_HAVE_CGNS)
1973   } else if (iscgns) {
1974     PetscCall(DMView_PlexCGNS(dm, viewer));
1975 #endif
1976   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
1977   /* Optionally view the partition */
1978   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
1979   if (flg) {
1980     Vec ranks;
1981     PetscCall(DMPlexCreateRankField(dm, &ranks));
1982     PetscCall(VecView(ranks, viewer));
1983     PetscCall(VecDestroy(&ranks));
1984   }
1985   /* Optionally view a label */
1986   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
1987   if (flg) {
1988     DMLabel label;
1989     Vec     val;
1990 
1991     PetscCall(DMGetLabel(dm, name, &label));
1992     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
1993     PetscCall(DMPlexCreateLabelField(dm, label, &val));
1994     PetscCall(VecView(val, viewer));
1995     PetscCall(VecDestroy(&val));
1996   }
1997   PetscFunctionReturn(PETSC_SUCCESS);
1998 }
1999 
2000 /*@
2001   DMPlexTopologyView - Saves a `DMPLEX` topology into a file
2002 
2003   Collective
2004 
2005   Input Parameters:
2006 + dm     - The `DM` whose topology is to be saved
2007 - viewer - The `PetscViewer` to save it in
2008 
2009   Level: advanced
2010 
2011 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`, `PetscViewer`
2012 @*/
2013 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
2014 {
2015   PetscBool ishdf5;
2016 
2017   PetscFunctionBegin;
2018   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2019   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2020   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2021   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
2022   if (ishdf5) {
2023 #if defined(PETSC_HAVE_HDF5)
2024     PetscViewerFormat format;
2025     PetscCall(PetscViewerGetFormat(viewer, &format));
2026     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2027       IS globalPointNumbering;
2028 
2029       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2030       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
2031       PetscCall(ISDestroy(&globalPointNumbering));
2032     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2033 #else
2034     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2035 #endif
2036   }
2037   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
2038   PetscFunctionReturn(PETSC_SUCCESS);
2039 }
2040 
2041 /*@
2042   DMPlexCoordinatesView - Saves `DMPLEX` coordinates into a file
2043 
2044   Collective
2045 
2046   Input Parameters:
2047 + dm     - The `DM` whose coordinates are to be saved
2048 - viewer - The `PetscViewer` for saving
2049 
2050   Level: advanced
2051 
2052 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`, `PetscViewer`
2053 @*/
2054 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
2055 {
2056   PetscBool ishdf5;
2057 
2058   PetscFunctionBegin;
2059   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2060   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2061   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2062   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2063   if (ishdf5) {
2064 #if defined(PETSC_HAVE_HDF5)
2065     PetscViewerFormat format;
2066     PetscCall(PetscViewerGetFormat(viewer, &format));
2067     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2068       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
2069     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2070 #else
2071     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2072 #endif
2073   }
2074   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2075   PetscFunctionReturn(PETSC_SUCCESS);
2076 }
2077 
2078 /*@
2079   DMPlexLabelsView - Saves `DMPLEX` labels into a file
2080 
2081   Collective
2082 
2083   Input Parameters:
2084 + dm     - The `DM` whose labels are to be saved
2085 - viewer - The `PetscViewer` for saving
2086 
2087   Level: advanced
2088 
2089 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`, `PetscViewer`
2090 @*/
2091 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
2092 {
2093   PetscBool ishdf5;
2094 
2095   PetscFunctionBegin;
2096   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2097   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2098   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2099   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
2100   if (ishdf5) {
2101 #if defined(PETSC_HAVE_HDF5)
2102     IS                globalPointNumbering;
2103     PetscViewerFormat format;
2104 
2105     PetscCall(PetscViewerGetFormat(viewer, &format));
2106     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2107       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2108       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
2109       PetscCall(ISDestroy(&globalPointNumbering));
2110     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2111 #else
2112     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2113 #endif
2114   }
2115   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
2116   PetscFunctionReturn(PETSC_SUCCESS);
2117 }
2118 
2119 /*@
2120   DMPlexSectionView - Saves a section associated with a `DMPLEX`
2121 
2122   Collective
2123 
2124   Input Parameters:
2125 + dm        - The `DM` that contains the topology on which the section to be saved is defined
2126 . viewer    - The `PetscViewer` for saving
2127 - sectiondm - The `DM` that contains the section to be saved, can be `NULL`
2128 
2129   Level: advanced
2130 
2131   Notes:
2132   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.
2133 
2134   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.
2135 
2136 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`, `PetscViewer`
2137 @*/
2138 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
2139 {
2140   PetscBool ishdf5;
2141 
2142   PetscFunctionBegin;
2143   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2144   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2145   if (!sectiondm) sectiondm = dm;
2146   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2147   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2148   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
2149   if (ishdf5) {
2150 #if defined(PETSC_HAVE_HDF5)
2151     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
2152 #else
2153     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2154 #endif
2155   }
2156   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
2157   PetscFunctionReturn(PETSC_SUCCESS);
2158 }
2159 
2160 /*@
2161   DMPlexGlobalVectorView - Saves a global vector
2162 
2163   Collective
2164 
2165   Input Parameters:
2166 + dm        - The `DM` that represents the topology
2167 . viewer    - The `PetscViewer` to save data with
2168 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2169 - vec       - The global vector to be saved
2170 
2171   Level: advanced
2172 
2173   Notes:
2174   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.
2175 
2176   Calling sequence:
2177 .vb
2178        DMCreate(PETSC_COMM_WORLD, &dm);
2179        DMSetType(dm, DMPLEX);
2180        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2181        DMClone(dm, &sectiondm);
2182        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2183        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2184        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2185        PetscSectionSetChart(section, pStart, pEnd);
2186        PetscSectionSetUp(section);
2187        DMSetLocalSection(sectiondm, section);
2188        PetscSectionDestroy(&section);
2189        DMGetGlobalVector(sectiondm, &vec);
2190        PetscObjectSetName((PetscObject)vec, "vec_name");
2191        DMPlexTopologyView(dm, viewer);
2192        DMPlexSectionView(dm, viewer, sectiondm);
2193        DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
2194        DMRestoreGlobalVector(sectiondm, &vec);
2195        DMDestroy(&sectiondm);
2196        DMDestroy(&dm);
2197 .ve
2198 
2199 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2200 @*/
2201 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2202 {
2203   PetscBool ishdf5;
2204 
2205   PetscFunctionBegin;
2206   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2207   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2208   if (!sectiondm) sectiondm = dm;
2209   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2210   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2211   /* Check consistency */
2212   {
2213     PetscSection section;
2214     PetscBool    includesConstraints;
2215     PetscInt     m, m1;
2216 
2217     PetscCall(VecGetLocalSize(vec, &m1));
2218     PetscCall(DMGetGlobalSection(sectiondm, &section));
2219     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2220     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2221     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2222     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2223   }
2224   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2225   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2226   if (ishdf5) {
2227 #if defined(PETSC_HAVE_HDF5)
2228     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2229 #else
2230     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2231 #endif
2232   }
2233   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2234   PetscFunctionReturn(PETSC_SUCCESS);
2235 }
2236 
2237 /*@
2238   DMPlexLocalVectorView - Saves a local vector
2239 
2240   Collective
2241 
2242   Input Parameters:
2243 + dm        - The `DM` that represents the topology
2244 . viewer    - The `PetscViewer` to save data with
2245 . sectiondm - The `DM` that contains the local section on which `vec` is defined, can be `NULL`
2246 - vec       - The local vector to be saved
2247 
2248   Level: advanced
2249 
2250   Note:
2251   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.
2252 
2253   Calling sequence:
2254 .vb
2255        DMCreate(PETSC_COMM_WORLD, &dm);
2256        DMSetType(dm, DMPLEX);
2257        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2258        DMClone(dm, &sectiondm);
2259        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2260        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2261        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2262        PetscSectionSetChart(section, pStart, pEnd);
2263        PetscSectionSetUp(section);
2264        DMSetLocalSection(sectiondm, section);
2265        DMGetLocalVector(sectiondm, &vec);
2266        PetscObjectSetName((PetscObject)vec, "vec_name");
2267        DMPlexTopologyView(dm, viewer);
2268        DMPlexSectionView(dm, viewer, sectiondm);
2269        DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2270        DMRestoreLocalVector(sectiondm, &vec);
2271        DMDestroy(&sectiondm);
2272        DMDestroy(&dm);
2273 .ve
2274 
2275 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2276 @*/
2277 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2278 {
2279   PetscBool ishdf5;
2280 
2281   PetscFunctionBegin;
2282   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2283   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2284   if (!sectiondm) sectiondm = dm;
2285   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2286   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2287   /* Check consistency */
2288   {
2289     PetscSection section;
2290     PetscBool    includesConstraints;
2291     PetscInt     m, m1;
2292 
2293     PetscCall(VecGetLocalSize(vec, &m1));
2294     PetscCall(DMGetLocalSection(sectiondm, &section));
2295     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2296     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2297     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2298     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2299   }
2300   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2301   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2302   if (ishdf5) {
2303 #if defined(PETSC_HAVE_HDF5)
2304     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2305 #else
2306     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2307 #endif
2308   }
2309   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2310   PetscFunctionReturn(PETSC_SUCCESS);
2311 }
2312 
2313 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2314 {
2315   PetscBool ishdf5;
2316 
2317   PetscFunctionBegin;
2318   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2319   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2320   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2321   if (ishdf5) {
2322 #if defined(PETSC_HAVE_HDF5)
2323     PetscViewerFormat format;
2324     PetscCall(PetscViewerGetFormat(viewer, &format));
2325     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2326       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2327     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2328       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2329     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2330     PetscFunctionReturn(PETSC_SUCCESS);
2331 #else
2332     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2333 #endif
2334   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2335 }
2336 
2337 /*@
2338   DMPlexTopologyLoad - Loads a topology into a `DMPLEX`
2339 
2340   Collective
2341 
2342   Input Parameters:
2343 + dm     - The `DM` into which the topology is loaded
2344 - viewer - The `PetscViewer` for the saved topology
2345 
2346   Output Parameter:
2347 . 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; `NULL` if unneeded
2348 
2349   Level: advanced
2350 
2351 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2352           `PetscViewer`, `PetscSF`
2353 @*/
2354 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2355 {
2356   PetscBool ishdf5;
2357 
2358   PetscFunctionBegin;
2359   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2360   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2361   if (globalToLocalPointSF) PetscAssertPointer(globalToLocalPointSF, 3);
2362   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2363   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2364   if (ishdf5) {
2365 #if defined(PETSC_HAVE_HDF5)
2366     PetscViewerFormat format;
2367     PetscCall(PetscViewerGetFormat(viewer, &format));
2368     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2369       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2370     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2371 #else
2372     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2373 #endif
2374   }
2375   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2376   PetscFunctionReturn(PETSC_SUCCESS);
2377 }
2378 
2379 /*@
2380   DMPlexCoordinatesLoad - Loads coordinates into a `DMPLEX`
2381 
2382   Collective
2383 
2384   Input Parameters:
2385 + dm                   - The `DM` into which the coordinates are loaded
2386 . viewer               - The `PetscViewer` for the saved coordinates
2387 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2388 
2389   Level: advanced
2390 
2391 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2392           `PetscSF`, `PetscViewer`
2393 @*/
2394 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2395 {
2396   PetscBool ishdf5;
2397 
2398   PetscFunctionBegin;
2399   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2400   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2401   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2402   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2403   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2404   if (ishdf5) {
2405 #if defined(PETSC_HAVE_HDF5)
2406     PetscViewerFormat format;
2407     PetscCall(PetscViewerGetFormat(viewer, &format));
2408     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2409       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2410     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2411 #else
2412     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2413 #endif
2414   }
2415   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2416   PetscFunctionReturn(PETSC_SUCCESS);
2417 }
2418 
2419 /*@
2420   DMPlexLabelsLoad - Loads labels into a `DMPLEX`
2421 
2422   Collective
2423 
2424   Input Parameters:
2425 + dm                   - The `DM` into which the labels are loaded
2426 . viewer               - The `PetscViewer` for the saved labels
2427 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading `dm` from viewer
2428 
2429   Level: advanced
2430 
2431   Note:
2432   The `PetscSF` argument must not be NULL if the `DM` is distributed, otherwise an error occurs.
2433 
2434 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2435           `PetscSF`, `PetscViewer`
2436 @*/
2437 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2438 {
2439   PetscBool ishdf5;
2440 
2441   PetscFunctionBegin;
2442   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2443   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2444   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2445   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2446   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2447   if (ishdf5) {
2448 #if defined(PETSC_HAVE_HDF5)
2449     PetscViewerFormat format;
2450 
2451     PetscCall(PetscViewerGetFormat(viewer, &format));
2452     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2453       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2454     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2455 #else
2456     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2457 #endif
2458   }
2459   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2460   PetscFunctionReturn(PETSC_SUCCESS);
2461 }
2462 
2463 /*@
2464   DMPlexSectionLoad - Loads section into a `DMPLEX`
2465 
2466   Collective
2467 
2468   Input Parameters:
2469 + dm                   - The `DM` that represents the topology
2470 . viewer               - The `PetscViewer` that represents the on-disk section (sectionA)
2471 . sectiondm            - The `DM` into which the on-disk section (sectionA) is migrated, can be `NULL`
2472 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad(`) when loading dm from viewer
2473 
2474   Output Parameters:
2475 + 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)
2476 - 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)
2477 
2478   Level: advanced
2479 
2480   Notes:
2481   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.
2482 
2483   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.
2484 
2485   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.
2486 
2487   Example using 2 processes:
2488 .vb
2489   NX (number of points on dm): 4
2490   sectionA                   : the on-disk section
2491   vecA                       : a vector associated with sectionA
2492   sectionB                   : sectiondm's local section constructed in this function
2493   vecB (local)               : a vector associated with sectiondm's local section
2494   vecB (global)              : a vector associated with sectiondm's global section
2495 
2496                                      rank 0    rank 1
2497   vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2498   sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2499   sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2500   sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2501   [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2502   sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2503   sectionB->atlasDof             :     1 0 1 | 1 3
2504   sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2505   vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2506   vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2507 .ve
2508   where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2509 
2510 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`, `PetscSF`, `PetscViewer`
2511 @*/
2512 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
2513 {
2514   PetscBool ishdf5;
2515 
2516   PetscFunctionBegin;
2517   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2518   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2519   if (!sectiondm) sectiondm = dm;
2520   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2521   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2522   if (globalDofSF) PetscAssertPointer(globalDofSF, 5);
2523   if (localDofSF) PetscAssertPointer(localDofSF, 6);
2524   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2525   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2526   if (ishdf5) {
2527 #if defined(PETSC_HAVE_HDF5)
2528     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2529 #else
2530     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2531 #endif
2532   }
2533   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2534   PetscFunctionReturn(PETSC_SUCCESS);
2535 }
2536 
2537 /*@
2538   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2539 
2540   Collective
2541 
2542   Input Parameters:
2543 + dm        - The `DM` that represents the topology
2544 . viewer    - The `PetscViewer` that represents the on-disk vector data
2545 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2546 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2547 - vec       - The global vector to set values of
2548 
2549   Level: advanced
2550 
2551   Notes:
2552   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.
2553 
2554   Calling sequence:
2555 .vb
2556        DMCreate(PETSC_COMM_WORLD, &dm);
2557        DMSetType(dm, DMPLEX);
2558        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2559        DMPlexTopologyLoad(dm, viewer, &sfX);
2560        DMClone(dm, &sectiondm);
2561        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2562        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2563        DMGetGlobalVector(sectiondm, &vec);
2564        PetscObjectSetName((PetscObject)vec, "vec_name");
2565        DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2566        DMRestoreGlobalVector(sectiondm, &vec);
2567        PetscSFDestroy(&gsf);
2568        PetscSFDestroy(&sfX);
2569        DMDestroy(&sectiondm);
2570        DMDestroy(&dm);
2571 .ve
2572 
2573 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2574           `PetscSF`, `PetscViewer`
2575 @*/
2576 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2577 {
2578   PetscBool ishdf5;
2579 
2580   PetscFunctionBegin;
2581   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2582   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2583   if (!sectiondm) sectiondm = dm;
2584   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2585   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2586   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2587   /* Check consistency */
2588   {
2589     PetscSection section;
2590     PetscBool    includesConstraints;
2591     PetscInt     m, m1;
2592 
2593     PetscCall(VecGetLocalSize(vec, &m1));
2594     PetscCall(DMGetGlobalSection(sectiondm, &section));
2595     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2596     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2597     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2598     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2599   }
2600   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2601   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2602   if (ishdf5) {
2603 #if defined(PETSC_HAVE_HDF5)
2604     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2605 #else
2606     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2607 #endif
2608   }
2609   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2610   PetscFunctionReturn(PETSC_SUCCESS);
2611 }
2612 
2613 /*@
2614   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2615 
2616   Collective
2617 
2618   Input Parameters:
2619 + dm        - The `DM` that represents the topology
2620 . viewer    - The `PetscViewer` that represents the on-disk vector data
2621 . sectiondm - The `DM` that contains the local section on which vec is defined, can be `NULL`
2622 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2623 - vec       - The local vector to set values of
2624 
2625   Level: advanced
2626 
2627   Notes:
2628   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.
2629 
2630   Calling sequence:
2631 .vb
2632        DMCreate(PETSC_COMM_WORLD, &dm);
2633        DMSetType(dm, DMPLEX);
2634        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2635        DMPlexTopologyLoad(dm, viewer, &sfX);
2636        DMClone(dm, &sectiondm);
2637        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2638        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2639        DMGetLocalVector(sectiondm, &vec);
2640        PetscObjectSetName((PetscObject)vec, "vec_name");
2641        DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2642        DMRestoreLocalVector(sectiondm, &vec);
2643        PetscSFDestroy(&lsf);
2644        PetscSFDestroy(&sfX);
2645        DMDestroy(&sectiondm);
2646        DMDestroy(&dm);
2647 .ve
2648 
2649 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2650           `PetscSF`, `PetscViewer`
2651 @*/
2652 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2653 {
2654   PetscBool ishdf5;
2655 
2656   PetscFunctionBegin;
2657   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2658   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2659   if (!sectiondm) sectiondm = dm;
2660   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2661   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2662   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2663   /* Check consistency */
2664   {
2665     PetscSection section;
2666     PetscBool    includesConstraints;
2667     PetscInt     m, m1;
2668 
2669     PetscCall(VecGetLocalSize(vec, &m1));
2670     PetscCall(DMGetLocalSection(sectiondm, &section));
2671     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2672     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2673     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2674     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2675   }
2676   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2677   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2678   if (ishdf5) {
2679 #if defined(PETSC_HAVE_HDF5)
2680     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2681 #else
2682     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2683 #endif
2684   }
2685   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2686   PetscFunctionReturn(PETSC_SUCCESS);
2687 }
2688 
2689 PetscErrorCode DMDestroy_Plex(DM dm)
2690 {
2691   DM_Plex *mesh = (DM_Plex *)dm->data;
2692 
2693   PetscFunctionBegin;
2694   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2695   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2696   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2697   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2698   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerivativeBoundaryValues_C", NULL));
2699   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2700   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2701   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2702   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2703   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2704   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2705   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetDefault_C", NULL));
2706   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetDefault_C", NULL));
2707   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetType_C", NULL));
2708   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetType_C", NULL));
2709   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2710   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2711   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetUseCeed_C", NULL));
2712   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetUseCeed_C", NULL));
2713   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", NULL));
2714   if (--mesh->refct > 0) PetscFunctionReturn(PETSC_SUCCESS);
2715   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2716   PetscCall(PetscFree(mesh->cones));
2717   PetscCall(PetscFree(mesh->coneOrientations));
2718   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2719   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2720   PetscCall(PetscFree(mesh->supports));
2721   PetscCall(PetscFree(mesh->cellTypes));
2722   PetscCall(DMPlexTransformDestroy(&mesh->tr));
2723   PetscCall(PetscFree(mesh->tetgenOpts));
2724   PetscCall(PetscFree(mesh->triangleOpts));
2725   PetscCall(PetscFree(mesh->transformType));
2726   PetscCall(PetscFree(mesh->distributionName));
2727   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2728   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2729   PetscCall(ISDestroy(&mesh->subpointIS));
2730   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2731   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2732   PetscCall(PetscSFDestroy(&mesh->periodic.face_sf));
2733   PetscCall(PetscSFDestroy(&mesh->periodic.composed_sf));
2734   PetscCall(ISDestroy(&mesh->periodic.periodic_points));
2735   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2736   PetscCall(ISDestroy(&mesh->anchorIS));
2737   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2738   PetscCall(PetscFree(mesh->parents));
2739   PetscCall(PetscFree(mesh->childIDs));
2740   PetscCall(PetscSectionDestroy(&mesh->childSection));
2741   PetscCall(PetscFree(mesh->children));
2742   PetscCall(DMDestroy(&mesh->referenceTree));
2743   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2744   PetscCall(PetscFree(mesh->neighbors));
2745   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2746   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2747   PetscCall(PetscFree(mesh));
2748   PetscFunctionReturn(PETSC_SUCCESS);
2749 }
2750 
2751 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2752 {
2753   PetscSection           sectionGlobal, sectionLocal;
2754   PetscInt               bs = -1, mbs;
2755   PetscInt               localSize, localStart = 0;
2756   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2757   MatType                mtype;
2758   ISLocalToGlobalMapping ltog;
2759 
2760   PetscFunctionBegin;
2761   PetscCall(MatInitializePackage());
2762   mtype = dm->mattype;
2763   PetscCall(DMGetLocalSection(dm, &sectionLocal));
2764   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2765   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2766   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2767   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2768   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2769   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2770   PetscCall(MatSetType(*J, mtype));
2771   PetscCall(MatSetFromOptions(*J));
2772   PetscCall(MatGetBlockSize(*J, &mbs));
2773   if (mbs > 1) bs = mbs;
2774   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2775   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2776   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2777   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2778   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2779   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2780   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2781   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2782   if (!isShell) {
2783     PetscBool fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2784     PetscInt *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2785     PetscInt  pStart, pEnd, p, dof, cdof, num_fields;
2786 
2787     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2788 
2789     PetscCall(PetscCalloc1(localSize, &pblocks));
2790     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2791     PetscCall(PetscSectionGetNumFields(sectionGlobal, &num_fields));
2792     for (p = pStart; p < pEnd; ++p) {
2793       switch (dm->blocking_type) {
2794       case DM_BLOCKING_TOPOLOGICAL_POINT: { // One block per topological point
2795         PetscInt bdof, offset;
2796 
2797         PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2798         PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2799         PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2800         for (PetscInt i = 0; i < dof - cdof; ++i) pblocks[offset - localStart + i] = dof - cdof;
2801         // Signal block concatenation
2802         if (dof - cdof && sectionLocal->blockStarts && !PetscBTLookup(sectionLocal->blockStarts, p)) pblocks[offset - localStart] = -(dof - cdof);
2803         dof  = dof < 0 ? -(dof + 1) : dof;
2804         bdof = cdof && (dof - cdof) ? 1 : dof;
2805         if (dof) {
2806           if (bs < 0) {
2807             bs = bdof;
2808           } else if (bs != bdof) {
2809             bs = 1;
2810           }
2811         }
2812       } break;
2813       case DM_BLOCKING_FIELD_NODE: {
2814         for (PetscInt field = 0; field < num_fields; field++) {
2815           PetscInt num_comp, bdof, offset;
2816           PetscCall(PetscSectionGetFieldComponents(sectionGlobal, field, &num_comp));
2817           PetscCall(PetscSectionGetFieldDof(sectionGlobal, p, field, &dof));
2818           if (dof < 0) continue;
2819           PetscCall(PetscSectionGetFieldOffset(sectionGlobal, p, field, &offset));
2820           PetscCall(PetscSectionGetFieldConstraintDof(sectionGlobal, p, field, &cdof));
2821           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);
2822           PetscInt num_nodes = dof / num_comp;
2823           for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = (dof - cdof) / num_nodes;
2824           // Handle possibly constant block size (unlikely)
2825           bdof = cdof && (dof - cdof) ? 1 : dof;
2826           if (dof) {
2827             if (bs < 0) {
2828               bs = bdof;
2829             } else if (bs != bdof) {
2830               bs = 1;
2831             }
2832           }
2833         }
2834       } break;
2835       }
2836     }
2837     /* Must have same blocksize on all procs (some might have no points) */
2838     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2839     bsLocal[1] = bs;
2840     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
2841     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2842     else bs = bsMinMax[0];
2843     bs = PetscMax(1, bs);
2844     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
2845     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2846       PetscCall(MatSetBlockSize(*J, bs));
2847       PetscCall(MatSetUp(*J));
2848     } else {
2849       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
2850       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2851       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2852     }
2853     { // Consolidate blocks
2854       PetscInt nblocks = 0;
2855       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
2856         if (pblocks[i] == 0) continue;
2857         // Negative block size indicates the blocks should be concatenated
2858         if (pblocks[i] < 0) {
2859           pblocks[i] = -pblocks[i];
2860           pblocks[nblocks - 1] += pblocks[i];
2861         } else {
2862           pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2863         }
2864         for (PetscInt j = 1; j < pblocks[i]; j++) PetscCheck(pblocks[i + j] == pblocks[i], PETSC_COMM_SELF, PETSC_ERR_PLIB, "Block of size %" PetscInt_FMT " mismatches entry %" PetscInt_FMT, pblocks[i], pblocks[i + j]);
2865       }
2866       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2867     }
2868     PetscCall(PetscFree(pblocks));
2869   }
2870   PetscCall(MatSetDM(*J, dm));
2871   PetscFunctionReturn(PETSC_SUCCESS);
2872 }
2873 
2874 /*@
2875   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2876 
2877   Not Collective
2878 
2879   Input Parameter:
2880 . dm - The `DMPLEX`
2881 
2882   Output Parameter:
2883 . subsection - The subdomain section
2884 
2885   Level: developer
2886 
2887 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscSection`
2888 @*/
2889 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2890 {
2891   DM_Plex *mesh = (DM_Plex *)dm->data;
2892 
2893   PetscFunctionBegin;
2894   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2895   if (!mesh->subdomainSection) {
2896     PetscSection section;
2897     PetscSF      sf;
2898 
2899     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
2900     PetscCall(DMGetLocalSection(dm, &section));
2901     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
2902     PetscCall(PetscSFDestroy(&sf));
2903   }
2904   *subsection = mesh->subdomainSection;
2905   PetscFunctionReturn(PETSC_SUCCESS);
2906 }
2907 
2908 /*@
2909   DMPlexGetChart - Return the interval for all mesh points [`pStart`, `pEnd`)
2910 
2911   Not Collective
2912 
2913   Input Parameter:
2914 . dm - The `DMPLEX`
2915 
2916   Output Parameters:
2917 + pStart - The first mesh point
2918 - pEnd   - The upper bound for mesh points
2919 
2920   Level: beginner
2921 
2922 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`
2923 @*/
2924 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
2925 {
2926   DM_Plex *mesh = (DM_Plex *)dm->data;
2927 
2928   PetscFunctionBegin;
2929   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2930   if (mesh->tr) PetscCall(DMPlexTransformGetChart(mesh->tr, pStart, pEnd));
2931   else PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
2932   PetscFunctionReturn(PETSC_SUCCESS);
2933 }
2934 
2935 /*@
2936   DMPlexSetChart - Set the interval for all mesh points [`pStart`, `pEnd`)
2937 
2938   Not Collective
2939 
2940   Input Parameters:
2941 + dm     - The `DMPLEX`
2942 . pStart - The first mesh point
2943 - pEnd   - The upper bound for mesh points
2944 
2945   Level: beginner
2946 
2947 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetChart()`
2948 @*/
2949 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
2950 {
2951   DM_Plex *mesh = (DM_Plex *)dm->data;
2952 
2953   PetscFunctionBegin;
2954   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2955   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
2956   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
2957   PetscCall(PetscFree(mesh->cellTypes));
2958   PetscFunctionReturn(PETSC_SUCCESS);
2959 }
2960 
2961 /*@
2962   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
2963 
2964   Not Collective
2965 
2966   Input Parameters:
2967 + dm - The `DMPLEX`
2968 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
2969 
2970   Output Parameter:
2971 . size - The cone size for point `p`
2972 
2973   Level: beginner
2974 
2975 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
2976 @*/
2977 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
2978 {
2979   DM_Plex *mesh = (DM_Plex *)dm->data;
2980 
2981   PetscFunctionBegin;
2982   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2983   PetscAssertPointer(size, 3);
2984   if (mesh->tr) PetscCall(DMPlexTransformGetConeSize(mesh->tr, p, size));
2985   else PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
2986   PetscFunctionReturn(PETSC_SUCCESS);
2987 }
2988 
2989 /*@
2990   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
2991 
2992   Not Collective
2993 
2994   Input Parameters:
2995 + dm   - The `DMPLEX`
2996 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
2997 - size - The cone size for point `p`
2998 
2999   Level: beginner
3000 
3001   Note:
3002   This should be called after `DMPlexSetChart()`.
3003 
3004 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
3005 @*/
3006 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
3007 {
3008   DM_Plex *mesh = (DM_Plex *)dm->data;
3009 
3010   PetscFunctionBegin;
3011   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3012   PetscCheck(!mesh->tr, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Cannot call DMPlexSetConeSize() on a mesh with a transform defined.");
3013   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
3014   PetscFunctionReturn(PETSC_SUCCESS);
3015 }
3016 
3017 /*@C
3018   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
3019 
3020   Not Collective
3021 
3022   Input Parameters:
3023 + dm - The `DMPLEX`
3024 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3025 
3026   Output Parameter:
3027 . cone - An array of points which are on the in-edges for point `p`
3028 
3029   Level: beginner
3030 
3031   Fortran Notes:
3032   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3033   `DMPlexRestoreCone()` is not needed/available in C.
3034 
3035 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`, `DMPlexRestoreCone()`
3036 @*/
3037 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
3038 {
3039   DM_Plex *mesh = (DM_Plex *)dm->data;
3040   PetscInt off;
3041 
3042   PetscFunctionBegin;
3043   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3044   PetscAssertPointer(cone, 3);
3045   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3046   *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3047   PetscFunctionReturn(PETSC_SUCCESS);
3048 }
3049 
3050 /*@C
3051   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
3052 
3053   Not Collective
3054 
3055   Input Parameters:
3056 + dm - The `DMPLEX`
3057 - p  - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3058 
3059   Output Parameters:
3060 + pConesSection - `PetscSection` describing the layout of `pCones`
3061 - pCones        - An array of points which are on the in-edges for the point set `p`
3062 
3063   Level: intermediate
3064 
3065 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`, `PetscSection`, `IS`
3066 @*/
3067 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
3068 {
3069   PetscSection cs, newcs;
3070   PetscInt    *cones;
3071   PetscInt    *newarr = NULL;
3072   PetscInt     n;
3073 
3074   PetscFunctionBegin;
3075   PetscCall(DMPlexGetCones(dm, &cones));
3076   PetscCall(DMPlexGetConeSection(dm, &cs));
3077   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
3078   if (pConesSection) *pConesSection = newcs;
3079   if (pCones) {
3080     PetscCall(PetscSectionGetStorageSize(newcs, &n));
3081     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
3082   }
3083   PetscFunctionReturn(PETSC_SUCCESS);
3084 }
3085 
3086 /*@
3087   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
3088 
3089   Not Collective
3090 
3091   Input Parameters:
3092 + dm     - The `DMPLEX`
3093 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3094 
3095   Output Parameter:
3096 . expandedPoints - An array of vertices recursively expanded from input points
3097 
3098   Level: advanced
3099 
3100   Notes:
3101   Like `DMPlexGetConeRecursive()` but returns only the 0-depth `IS` (i.e. vertices only) and no sections.
3102 
3103   There is no corresponding Restore function, just call `ISDestroy()` on the returned `IS` to deallocate.
3104 
3105 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`,
3106           `DMPlexGetDepth()`, `IS`
3107 @*/
3108 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
3109 {
3110   IS      *expandedPointsAll;
3111   PetscInt depth;
3112 
3113   PetscFunctionBegin;
3114   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3115   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3116   PetscAssertPointer(expandedPoints, 3);
3117   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3118   *expandedPoints = expandedPointsAll[0];
3119   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
3120   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3121   PetscFunctionReturn(PETSC_SUCCESS);
3122 }
3123 
3124 /*@
3125   DMPlexGetConeRecursive - Expand each given point into its cone points and do that recursively until we end up just with vertices (DAG points of depth 0, i.e. without cones).
3126 
3127   Not Collective
3128 
3129   Input Parameters:
3130 + dm     - The `DMPLEX`
3131 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3132 
3133   Output Parameters:
3134 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3135 . expandedPoints - (optional) An array of index sets with recursively expanded cones
3136 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3137 
3138   Level: advanced
3139 
3140   Notes:
3141   Like `DMPlexGetConeTuple()` but recursive.
3142 
3143   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.
3144   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
3145 
3146   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\:
3147   (1) DAG points in `expandedPoints`[d+1] with `depth` d+1 to their cone points in `expandedPoints`[d];
3148   (2) DAG points in `expandedPoints`[d+1] with `depth` in [0,d] to the same points in `expandedPoints`[d].
3149 
3150 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3151           `DMPlexGetDepth()`, `PetscSection`, `IS`
3152 @*/
3153 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3154 {
3155   const PetscInt *arr0 = NULL, *cone = NULL;
3156   PetscInt       *arr = NULL, *newarr = NULL;
3157   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
3158   IS             *expandedPoints_;
3159   PetscSection   *sections_;
3160 
3161   PetscFunctionBegin;
3162   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3163   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3164   if (depth) PetscAssertPointer(depth, 3);
3165   if (expandedPoints) PetscAssertPointer(expandedPoints, 4);
3166   if (sections) PetscAssertPointer(sections, 5);
3167   PetscCall(ISGetLocalSize(points, &n));
3168   PetscCall(ISGetIndices(points, &arr0));
3169   PetscCall(DMPlexGetDepth(dm, &depth_));
3170   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
3171   PetscCall(PetscCalloc1(depth_, &sections_));
3172   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
3173   for (d = depth_ - 1; d >= 0; d--) {
3174     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
3175     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
3176     for (i = 0; i < n; i++) {
3177       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
3178       if (arr[i] >= start && arr[i] < end) {
3179         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
3180         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
3181       } else {
3182         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
3183       }
3184     }
3185     PetscCall(PetscSectionSetUp(sections_[d]));
3186     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
3187     PetscCall(PetscMalloc1(newn, &newarr));
3188     for (i = 0; i < n; i++) {
3189       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
3190       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
3191       if (cn > 1) {
3192         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
3193         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
3194       } else {
3195         newarr[co] = arr[i];
3196       }
3197     }
3198     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
3199     arr = newarr;
3200     n   = newn;
3201   }
3202   PetscCall(ISRestoreIndices(points, &arr0));
3203   *depth = depth_;
3204   if (expandedPoints) *expandedPoints = expandedPoints_;
3205   else {
3206     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
3207     PetscCall(PetscFree(expandedPoints_));
3208   }
3209   if (sections) *sections = sections_;
3210   else {
3211     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
3212     PetscCall(PetscFree(sections_));
3213   }
3214   PetscFunctionReturn(PETSC_SUCCESS);
3215 }
3216 
3217 /*@
3218   DMPlexRestoreConeRecursive - Deallocates arrays created by `DMPlexGetConeRecursive()`
3219 
3220   Not Collective
3221 
3222   Input Parameters:
3223 + dm     - The `DMPLEX`
3224 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3225 
3226   Output Parameters:
3227 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3228 . expandedPoints - (optional) An array of recursively expanded cones
3229 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3230 
3231   Level: advanced
3232 
3233   Note:
3234   See `DMPlexGetConeRecursive()`
3235 
3236 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3237           `DMPlexGetDepth()`, `IS`, `PetscSection`
3238 @*/
3239 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3240 {
3241   PetscInt d, depth_;
3242 
3243   PetscFunctionBegin;
3244   PetscCall(DMPlexGetDepth(dm, &depth_));
3245   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
3246   if (depth) *depth = 0;
3247   if (expandedPoints) {
3248     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
3249     PetscCall(PetscFree(*expandedPoints));
3250   }
3251   if (sections) {
3252     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
3253     PetscCall(PetscFree(*sections));
3254   }
3255   PetscFunctionReturn(PETSC_SUCCESS);
3256 }
3257 
3258 /*@
3259   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
3260 
3261   Not Collective
3262 
3263   Input Parameters:
3264 + dm   - The `DMPLEX`
3265 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3266 - cone - An array of points which are on the in-edges for point `p`
3267 
3268   Level: beginner
3269 
3270   Note:
3271   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3272 
3273 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3274 @*/
3275 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3276 {
3277   DM_Plex *mesh = (DM_Plex *)dm->data;
3278   PetscInt dof, off, c;
3279 
3280   PetscFunctionBegin;
3281   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3282   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3283   if (dof) PetscAssertPointer(cone, 3);
3284   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3285   if (PetscDefined(USE_DEBUG)) {
3286     PetscInt pStart, pEnd;
3287     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3288     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);
3289     for (c = 0; c < dof; ++c) {
3290       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);
3291       mesh->cones[off + c] = cone[c];
3292     }
3293   } else {
3294     for (c = 0; c < dof; ++c) mesh->cones[off + c] = cone[c];
3295   }
3296   PetscFunctionReturn(PETSC_SUCCESS);
3297 }
3298 
3299 /*@C
3300   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3301 
3302   Not Collective
3303 
3304   Input Parameters:
3305 + dm - The `DMPLEX`
3306 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3307 
3308   Output Parameter:
3309 . coneOrientation - An array of orientations which are on the in-edges for point `p`. An orientation is an
3310                     integer giving the prescription for cone traversal.
3311 
3312   Level: beginner
3313 
3314   Note:
3315   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3316   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3317   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3318   with the identity.
3319 
3320   Fortran Notes:
3321   You must also call `DMPlexRestoreConeOrientation()` after you finish using the returned array.
3322   `DMPlexRestoreConeOrientation()` is not needed/available in C.
3323 
3324 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3325 @*/
3326 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3327 {
3328   DM_Plex *mesh = (DM_Plex *)dm->data;
3329   PetscInt off;
3330 
3331   PetscFunctionBegin;
3332   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3333   if (PetscDefined(USE_DEBUG)) {
3334     PetscInt dof;
3335     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3336     if (dof) PetscAssertPointer(coneOrientation, 3);
3337   }
3338   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3339 
3340   *coneOrientation = &mesh->coneOrientations[off];
3341   PetscFunctionReturn(PETSC_SUCCESS);
3342 }
3343 
3344 /*@
3345   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3346 
3347   Not Collective
3348 
3349   Input Parameters:
3350 + dm              - The `DMPLEX`
3351 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3352 - coneOrientation - An array of orientations
3353 
3354   Level: beginner
3355 
3356   Notes:
3357   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3358 
3359   The meaning of coneOrientation is detailed in `DMPlexGetConeOrientation()`.
3360 
3361 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3362 @*/
3363 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3364 {
3365   DM_Plex *mesh = (DM_Plex *)dm->data;
3366   PetscInt pStart, pEnd;
3367   PetscInt dof, off, c;
3368 
3369   PetscFunctionBegin;
3370   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3371   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3372   if (dof) PetscAssertPointer(coneOrientation, 3);
3373   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3374   if (PetscDefined(USE_DEBUG)) {
3375     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3376     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);
3377     for (c = 0; c < dof; ++c) {
3378       PetscInt cdof, o = coneOrientation[c];
3379 
3380       PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3381       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);
3382       mesh->coneOrientations[off + c] = o;
3383     }
3384   } else {
3385     for (c = 0; c < dof; ++c) mesh->coneOrientations[off + c] = coneOrientation[c];
3386   }
3387   PetscFunctionReturn(PETSC_SUCCESS);
3388 }
3389 
3390 /*@
3391   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3392 
3393   Not Collective
3394 
3395   Input Parameters:
3396 + dm        - The `DMPLEX`
3397 . p         - The point, which must lie in the chart set with `DMPlexSetChart()`
3398 . conePos   - The local index in the cone where the point should be put
3399 - conePoint - The mesh point to insert
3400 
3401   Level: beginner
3402 
3403 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3404 @*/
3405 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3406 {
3407   DM_Plex *mesh = (DM_Plex *)dm->data;
3408   PetscInt pStart, pEnd;
3409   PetscInt dof, off;
3410 
3411   PetscFunctionBegin;
3412   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3413   if (PetscDefined(USE_DEBUG)) {
3414     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3415     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);
3416     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);
3417     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3418     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);
3419   }
3420   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3421   mesh->cones[off + conePos] = conePoint;
3422   PetscFunctionReturn(PETSC_SUCCESS);
3423 }
3424 
3425 /*@
3426   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3427 
3428   Not Collective
3429 
3430   Input Parameters:
3431 + dm              - The `DMPLEX`
3432 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3433 . conePos         - The local index in the cone where the point should be put
3434 - coneOrientation - The point orientation to insert
3435 
3436   Level: beginner
3437 
3438   Note:
3439   The meaning of coneOrientation values is detailed in `DMPlexGetConeOrientation()`.
3440 
3441 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3442 @*/
3443 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3444 {
3445   DM_Plex *mesh = (DM_Plex *)dm->data;
3446   PetscInt pStart, pEnd;
3447   PetscInt dof, off;
3448 
3449   PetscFunctionBegin;
3450   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3451   if (PetscDefined(USE_DEBUG)) {
3452     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3453     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);
3454     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3455     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);
3456   }
3457   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3458   mesh->coneOrientations[off + conePos] = coneOrientation;
3459   PetscFunctionReturn(PETSC_SUCCESS);
3460 }
3461 
3462 /*@C
3463   DMPlexGetOrientedCone - Return the points and orientations on the in-edges for this point in the DAG
3464 
3465   Not collective
3466 
3467   Input Parameters:
3468 + dm - The DMPlex
3469 - p  - The point, which must lie in the chart set with DMPlexSetChart()
3470 
3471   Output Parameters:
3472 + cone - An array of points which are on the in-edges for point `p`
3473 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3474         integer giving the prescription for cone traversal.
3475 
3476   Level: beginner
3477 
3478   Notes:
3479   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3480   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3481   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3482   with the identity.
3483 
3484   Fortran Notes:
3485   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3486   `DMPlexRestoreCone()` is not needed/available in C.
3487 
3488 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3489 @*/
3490 PetscErrorCode DMPlexGetOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3491 {
3492   DM_Plex *mesh = (DM_Plex *)dm->data;
3493 
3494   PetscFunctionBegin;
3495   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3496   if (mesh->tr) {
3497     PetscCall(DMPlexTransformGetCone(mesh->tr, p, cone, ornt));
3498   } else {
3499     PetscInt off;
3500     if (PetscDefined(USE_DEBUG)) {
3501       PetscInt dof;
3502       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3503       if (dof) {
3504         if (cone) PetscAssertPointer(cone, 3);
3505         if (ornt) PetscAssertPointer(ornt, 4);
3506       }
3507     }
3508     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3509     if (cone) *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3510     if (ornt) *ornt = PetscSafePointerPlusOffset(mesh->coneOrientations, off);
3511   }
3512   PetscFunctionReturn(PETSC_SUCCESS);
3513 }
3514 
3515 /*@C
3516   DMPlexRestoreOrientedCone - Restore the points and orientations on the in-edges for this point in the DAG
3517 
3518   Not Collective
3519 
3520   Input Parameters:
3521 + dm   - The DMPlex
3522 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3523 . cone - An array of points which are on the in-edges for point p
3524 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3525         integer giving the prescription for cone traversal.
3526 
3527   Level: beginner
3528 
3529   Notes:
3530   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3531   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3532   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3533   with the identity.
3534 
3535   Fortran Notes:
3536   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3537   `DMPlexRestoreCone()` is not needed/available in C.
3538 
3539 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3540 @*/
3541 PetscErrorCode DMPlexRestoreOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3542 {
3543   DM_Plex *mesh = (DM_Plex *)dm->data;
3544 
3545   PetscFunctionBegin;
3546   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3547   if (mesh->tr) PetscCall(DMPlexTransformRestoreCone(mesh->tr, p, cone, ornt));
3548   PetscFunctionReturn(PETSC_SUCCESS);
3549 }
3550 
3551 /*@
3552   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3553 
3554   Not Collective
3555 
3556   Input Parameters:
3557 + dm - The `DMPLEX`
3558 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3559 
3560   Output Parameter:
3561 . size - The support size for point `p`
3562 
3563   Level: beginner
3564 
3565 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3566 @*/
3567 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3568 {
3569   DM_Plex *mesh = (DM_Plex *)dm->data;
3570 
3571   PetscFunctionBegin;
3572   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3573   PetscAssertPointer(size, 3);
3574   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3575   PetscFunctionReturn(PETSC_SUCCESS);
3576 }
3577 
3578 /*@
3579   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3580 
3581   Not Collective
3582 
3583   Input Parameters:
3584 + dm   - The `DMPLEX`
3585 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3586 - size - The support size for point `p`
3587 
3588   Level: beginner
3589 
3590   Note:
3591   This should be called after `DMPlexSetChart()`.
3592 
3593 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3594 @*/
3595 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3596 {
3597   DM_Plex *mesh = (DM_Plex *)dm->data;
3598 
3599   PetscFunctionBegin;
3600   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3601   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3602   PetscFunctionReturn(PETSC_SUCCESS);
3603 }
3604 
3605 /*@C
3606   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3607 
3608   Not Collective
3609 
3610   Input Parameters:
3611 + dm - The `DMPLEX`
3612 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3613 
3614   Output Parameter:
3615 . support - An array of points which are on the out-edges for point `p`
3616 
3617   Level: beginner
3618 
3619   Fortran Notes:
3620   You must also call `DMPlexRestoreSupport()` after you finish using the returned array.
3621   `DMPlexRestoreSupport()` is not needed/available in C.
3622 
3623 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3624 @*/
3625 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3626 {
3627   DM_Plex *mesh = (DM_Plex *)dm->data;
3628   PetscInt off;
3629 
3630   PetscFunctionBegin;
3631   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3632   PetscAssertPointer(support, 3);
3633   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3634   *support = PetscSafePointerPlusOffset(mesh->supports, off);
3635   PetscFunctionReturn(PETSC_SUCCESS);
3636 }
3637 
3638 /*@
3639   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3640 
3641   Not Collective
3642 
3643   Input Parameters:
3644 + dm      - The `DMPLEX`
3645 . p       - The point, which must lie in the chart set with `DMPlexSetChart()`
3646 - support - An array of points which are on the out-edges for point `p`
3647 
3648   Level: beginner
3649 
3650   Note:
3651   This should be called after all calls to `DMPlexSetSupportSize()` and `DMSetUp()`.
3652 
3653 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3654 @*/
3655 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3656 {
3657   DM_Plex *mesh = (DM_Plex *)dm->data;
3658   PetscInt pStart, pEnd;
3659   PetscInt dof, off, c;
3660 
3661   PetscFunctionBegin;
3662   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3663   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3664   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3665   if (dof) PetscAssertPointer(support, 3);
3666   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3667   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);
3668   for (c = 0; c < dof; ++c) {
3669     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);
3670     mesh->supports[off + c] = support[c];
3671   }
3672   PetscFunctionReturn(PETSC_SUCCESS);
3673 }
3674 
3675 /*@
3676   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3677 
3678   Not Collective
3679 
3680   Input Parameters:
3681 + dm           - The `DMPLEX`
3682 . p            - The point, which must lie in the chart set with `DMPlexSetChart()`
3683 . supportPos   - The local index in the cone where the point should be put
3684 - supportPoint - The mesh point to insert
3685 
3686   Level: beginner
3687 
3688 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3689 @*/
3690 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3691 {
3692   DM_Plex *mesh = (DM_Plex *)dm->data;
3693   PetscInt pStart, pEnd;
3694   PetscInt dof, off;
3695 
3696   PetscFunctionBegin;
3697   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3698   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3699   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3700   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3701   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);
3702   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);
3703   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);
3704   mesh->supports[off + supportPos] = supportPoint;
3705   PetscFunctionReturn(PETSC_SUCCESS);
3706 }
3707 
3708 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3709 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3710 {
3711   switch (ct) {
3712   case DM_POLYTOPE_SEGMENT:
3713     if (o == -1) return -2;
3714     break;
3715   case DM_POLYTOPE_TRIANGLE:
3716     if (o == -3) return -1;
3717     if (o == -2) return -3;
3718     if (o == -1) return -2;
3719     break;
3720   case DM_POLYTOPE_QUADRILATERAL:
3721     if (o == -4) return -2;
3722     if (o == -3) return -1;
3723     if (o == -2) return -4;
3724     if (o == -1) return -3;
3725     break;
3726   default:
3727     return o;
3728   }
3729   return o;
3730 }
3731 
3732 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3733 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3734 {
3735   switch (ct) {
3736   case DM_POLYTOPE_SEGMENT:
3737     if ((o == -2) || (o == 1)) return -1;
3738     if (o == -1) return 0;
3739     break;
3740   case DM_POLYTOPE_TRIANGLE:
3741     if (o == -3) return -2;
3742     if (o == -2) return -1;
3743     if (o == -1) return -3;
3744     break;
3745   case DM_POLYTOPE_QUADRILATERAL:
3746     if (o == -4) return -2;
3747     if (o == -3) return -1;
3748     if (o == -2) return -4;
3749     if (o == -1) return -3;
3750     break;
3751   default:
3752     return o;
3753   }
3754   return o;
3755 }
3756 
3757 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3758 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3759 {
3760   PetscInt pStart, pEnd, p;
3761 
3762   PetscFunctionBegin;
3763   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3764   for (p = pStart; p < pEnd; ++p) {
3765     const PetscInt *cone, *ornt;
3766     PetscInt        coneSize, c;
3767 
3768     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3769     PetscCall(DMPlexGetCone(dm, p, &cone));
3770     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3771     for (c = 0; c < coneSize; ++c) {
3772       DMPolytopeType ct;
3773       const PetscInt o = ornt[c];
3774 
3775       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3776       switch (ct) {
3777       case DM_POLYTOPE_SEGMENT:
3778         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3779         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3780         break;
3781       case DM_POLYTOPE_TRIANGLE:
3782         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3783         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3784         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3785         break;
3786       case DM_POLYTOPE_QUADRILATERAL:
3787         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3788         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3789         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3790         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3791         break;
3792       default:
3793         break;
3794       }
3795     }
3796   }
3797   PetscFunctionReturn(PETSC_SUCCESS);
3798 }
3799 
3800 static inline PetscErrorCode DMPlexGetTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3801 {
3802   DM_Plex *mesh = (DM_Plex *)dm->data;
3803 
3804   PetscFunctionBeginHot;
3805   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3806     if (useCone) {
3807       PetscCall(DMPlexGetConeSize(dm, p, size));
3808       PetscCall(DMPlexGetOrientedCone(dm, p, arr, ornt));
3809     } else {
3810       PetscCall(DMPlexGetSupportSize(dm, p, size));
3811       PetscCall(DMPlexGetSupport(dm, p, arr));
3812     }
3813   } else {
3814     if (useCone) {
3815       const PetscSection s   = mesh->coneSection;
3816       const PetscInt     ps  = p - s->pStart;
3817       const PetscInt     off = s->atlasOff[ps];
3818 
3819       *size = s->atlasDof[ps];
3820       *arr  = mesh->cones + off;
3821       *ornt = mesh->coneOrientations + off;
3822     } else {
3823       const PetscSection s   = mesh->supportSection;
3824       const PetscInt     ps  = p - s->pStart;
3825       const PetscInt     off = s->atlasOff[ps];
3826 
3827       *size = s->atlasDof[ps];
3828       *arr  = mesh->supports + off;
3829     }
3830   }
3831   PetscFunctionReturn(PETSC_SUCCESS);
3832 }
3833 
3834 static inline PetscErrorCode DMPlexRestoreTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3835 {
3836   DM_Plex *mesh = (DM_Plex *)dm->data;
3837 
3838   PetscFunctionBeginHot;
3839   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3840     if (useCone) PetscCall(DMPlexRestoreOrientedCone(dm, p, arr, ornt));
3841   }
3842   PetscFunctionReturn(PETSC_SUCCESS);
3843 }
3844 
3845 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3846 {
3847   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3848   PetscInt       *closure;
3849   const PetscInt *tmp = NULL, *tmpO = NULL;
3850   PetscInt        off = 0, tmpSize, t;
3851 
3852   PetscFunctionBeginHot;
3853   if (ornt) {
3854     PetscCall(DMPlexGetCellType(dm, p, &ct));
3855     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;
3856   }
3857   if (*points) {
3858     closure = *points;
3859   } else {
3860     PetscInt maxConeSize, maxSupportSize;
3861     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3862     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
3863   }
3864   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3865   if (ct == DM_POLYTOPE_UNKNOWN) {
3866     closure[off++] = p;
3867     closure[off++] = 0;
3868     for (t = 0; t < tmpSize; ++t) {
3869       closure[off++] = tmp[t];
3870       closure[off++] = tmpO ? tmpO[t] : 0;
3871     }
3872   } else {
3873     const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, ornt);
3874 
3875     /* We assume that cells with a valid type have faces with a valid type */
3876     closure[off++] = p;
3877     closure[off++] = ornt;
3878     for (t = 0; t < tmpSize; ++t) {
3879       DMPolytopeType ft;
3880 
3881       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3882       closure[off++] = tmp[arr[t]];
3883       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3884     }
3885   }
3886   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3887   if (numPoints) *numPoints = tmpSize + 1;
3888   if (points) *points = closure;
3889   PetscFunctionReturn(PETSC_SUCCESS);
3890 }
3891 
3892 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
3893 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3894 {
3895   const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, o);
3896   const PetscInt *cone, *ornt;
3897   PetscInt       *pts, *closure = NULL;
3898   DMPolytopeType  ft;
3899   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3900   PetscInt        dim, coneSize, c, d, clSize, cl;
3901 
3902   PetscFunctionBeginHot;
3903   PetscCall(DMGetDimension(dm, &dim));
3904   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3905   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3906   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
3907   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
3908   maxSize       = PetscMax(coneSeries, supportSeries);
3909   if (*points) {
3910     pts = *points;
3911   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
3912   c        = 0;
3913   pts[c++] = point;
3914   pts[c++] = o;
3915   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
3916   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
3917   for (cl = 0; cl < clSize * 2; cl += 2) {
3918     pts[c++] = closure[cl];
3919     pts[c++] = closure[cl + 1];
3920   }
3921   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
3922   for (cl = 0; cl < clSize * 2; cl += 2) {
3923     pts[c++] = closure[cl];
3924     pts[c++] = closure[cl + 1];
3925   }
3926   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3927   for (d = 2; d < coneSize; ++d) {
3928     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
3929     pts[c++] = cone[arr[d * 2 + 0]];
3930     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
3931   }
3932   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3933   if (dim >= 3) {
3934     for (d = 2; d < coneSize; ++d) {
3935       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
3936       const PetscInt *fcone, *fornt;
3937       PetscInt        fconeSize, fc, i;
3938 
3939       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
3940       const PetscInt *farr = DMPolytopeTypeGetArrangement(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
3941       PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
3942       for (fc = 0; fc < fconeSize; ++fc) {
3943         const PetscInt cp = fcone[farr[fc * 2 + 0]];
3944         const PetscInt co = farr[fc * 2 + 1];
3945 
3946         for (i = 0; i < c; i += 2)
3947           if (pts[i] == cp) break;
3948         if (i == c) {
3949           PetscCall(DMPlexGetCellType(dm, cp, &ft));
3950           pts[c++] = cp;
3951           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
3952         }
3953       }
3954       PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
3955     }
3956   }
3957   *numPoints = c / 2;
3958   *points    = pts;
3959   PetscFunctionReturn(PETSC_SUCCESS);
3960 }
3961 
3962 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3963 {
3964   DMPolytopeType ct;
3965   PetscInt      *closure, *fifo;
3966   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
3967   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
3968   PetscInt       depth, maxSize;
3969 
3970   PetscFunctionBeginHot;
3971   PetscCall(DMPlexGetDepth(dm, &depth));
3972   if (depth == 1) {
3973     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
3974     PetscFunctionReturn(PETSC_SUCCESS);
3975   }
3976   PetscCall(DMPlexGetCellType(dm, p, &ct));
3977   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;
3978   if (DMPolytopeTypeIsHybrid(ct) && ct != DM_POLYTOPE_POINT_PRISM_TENSOR) {
3979     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
3980     PetscFunctionReturn(PETSC_SUCCESS);
3981   }
3982   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3983   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
3984   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
3985   maxSize       = PetscMax(coneSeries, supportSeries);
3986   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3987   if (*points) {
3988     closure = *points;
3989   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
3990   closure[closureSize++] = p;
3991   closure[closureSize++] = ornt;
3992   fifo[fifoSize++]       = p;
3993   fifo[fifoSize++]       = ornt;
3994   fifo[fifoSize++]       = ct;
3995   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
3996   while (fifoSize - fifoStart) {
3997     const PetscInt       q    = fifo[fifoStart++];
3998     const PetscInt       o    = fifo[fifoStart++];
3999     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
4000     const PetscInt      *qarr = DMPolytopeTypeGetArrangement(qt, o);
4001     const PetscInt      *tmp, *tmpO = NULL;
4002     PetscInt             tmpSize, t;
4003 
4004     if (PetscDefined(USE_DEBUG)) {
4005       PetscInt nO = DMPolytopeTypeGetNumArrangements(qt) / 2;
4006       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);
4007     }
4008     PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4009     for (t = 0; t < tmpSize; ++t) {
4010       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
4011       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
4012       const PetscInt cp = tmp[ip];
4013       PetscCall(DMPlexGetCellType(dm, cp, &ct));
4014       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
4015       PetscInt       c;
4016 
4017       /* Check for duplicate */
4018       for (c = 0; c < closureSize; c += 2) {
4019         if (closure[c] == cp) break;
4020       }
4021       if (c == closureSize) {
4022         closure[closureSize++] = cp;
4023         closure[closureSize++] = co;
4024         fifo[fifoSize++]       = cp;
4025         fifo[fifoSize++]       = co;
4026         fifo[fifoSize++]       = ct;
4027       }
4028     }
4029     PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4030   }
4031   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
4032   if (numPoints) *numPoints = closureSize / 2;
4033   if (points) *points = closure;
4034   PetscFunctionReturn(PETSC_SUCCESS);
4035 }
4036 
4037 /*@C
4038   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
4039 
4040   Not Collective
4041 
4042   Input Parameters:
4043 + dm      - The `DMPLEX`
4044 . p       - The mesh point
4045 - useCone - `PETSC_TRUE` for the closure, otherwise return the star
4046 
4047   Input/Output Parameter:
4048 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
4049            if `NULL` on input, internal storage will be returned, otherwise the provided array is used
4050 
4051   Output Parameter:
4052 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
4053 
4054   Level: beginner
4055 
4056   Note:
4057   If using internal storage (points is `NULL` on input), each call overwrites the last output.
4058 
4059   Fortran Notes:
4060   The `numPoints` argument is not present in the Fortran binding since it is internal to the array.
4061 
4062 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4063 @*/
4064 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4065 {
4066   PetscFunctionBeginHot;
4067   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4068   if (numPoints) PetscAssertPointer(numPoints, 4);
4069   if (points) PetscAssertPointer(points, 5);
4070   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
4071   PetscFunctionReturn(PETSC_SUCCESS);
4072 }
4073 
4074 /*@C
4075   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
4076 
4077   Not Collective
4078 
4079   Input Parameters:
4080 + dm        - The `DMPLEX`
4081 . p         - The mesh point
4082 . useCone   - `PETSC_TRUE` for the closure, otherwise return the star
4083 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
4084 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
4085 
4086   Level: beginner
4087 
4088   Note:
4089   If not using internal storage (points is not `NULL` on input), this call is unnecessary
4090 
4091 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4092 @*/
4093 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4094 {
4095   PetscFunctionBeginHot;
4096   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4097   if (numPoints) *numPoints = 0;
4098   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
4099   PetscFunctionReturn(PETSC_SUCCESS);
4100 }
4101 
4102 /*@
4103   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
4104 
4105   Not Collective
4106 
4107   Input Parameter:
4108 . dm - The `DMPLEX`
4109 
4110   Output Parameters:
4111 + maxConeSize    - The maximum number of in-edges
4112 - maxSupportSize - The maximum number of out-edges
4113 
4114   Level: beginner
4115 
4116 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
4117 @*/
4118 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
4119 {
4120   DM_Plex *mesh = (DM_Plex *)dm->data;
4121 
4122   PetscFunctionBegin;
4123   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4124   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
4125   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
4126   PetscFunctionReturn(PETSC_SUCCESS);
4127 }
4128 
4129 PetscErrorCode DMSetUp_Plex(DM dm)
4130 {
4131   DM_Plex *mesh = (DM_Plex *)dm->data;
4132   PetscInt size, maxSupportSize;
4133 
4134   PetscFunctionBegin;
4135   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4136   PetscCall(PetscSectionSetUp(mesh->coneSection));
4137   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
4138   PetscCall(PetscMalloc1(size, &mesh->cones));
4139   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
4140   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4141   if (maxSupportSize) {
4142     PetscCall(PetscSectionSetUp(mesh->supportSection));
4143     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
4144     PetscCall(PetscMalloc1(size, &mesh->supports));
4145   }
4146   PetscFunctionReturn(PETSC_SUCCESS);
4147 }
4148 
4149 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
4150 {
4151   PetscFunctionBegin;
4152   if (subdm) PetscCall(DMClone(dm, subdm));
4153   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, NULL, NULL, is, subdm));
4154   if (subdm) (*subdm)->useNatural = dm->useNatural;
4155   if (dm->useNatural && dm->sfMigration) {
4156     PetscSF sfNatural;
4157 
4158     (*subdm)->sfMigration = dm->sfMigration;
4159     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
4160     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
4161     (*subdm)->sfNatural = sfNatural;
4162   }
4163   PetscFunctionReturn(PETSC_SUCCESS);
4164 }
4165 
4166 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
4167 {
4168   PetscInt i = 0;
4169 
4170   PetscFunctionBegin;
4171   PetscCall(DMClone(dms[0], superdm));
4172   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
4173   (*superdm)->useNatural = PETSC_FALSE;
4174   for (i = 0; i < len; i++) {
4175     if (dms[i]->useNatural && dms[i]->sfMigration) {
4176       PetscSF sfNatural;
4177 
4178       (*superdm)->sfMigration = dms[i]->sfMigration;
4179       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
4180       (*superdm)->useNatural = PETSC_TRUE;
4181       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
4182       (*superdm)->sfNatural = sfNatural;
4183       break;
4184     }
4185   }
4186   PetscFunctionReturn(PETSC_SUCCESS);
4187 }
4188 
4189 /*@
4190   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
4191 
4192   Not Collective
4193 
4194   Input Parameter:
4195 . dm - The `DMPLEX`
4196 
4197   Level: beginner
4198 
4199   Note:
4200   This should be called after all calls to `DMPlexSetCone()`
4201 
4202 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
4203 @*/
4204 PetscErrorCode DMPlexSymmetrize(DM dm)
4205 {
4206   DM_Plex  *mesh = (DM_Plex *)dm->data;
4207   PetscInt *offsets;
4208   PetscInt  supportSize;
4209   PetscInt  pStart, pEnd, p;
4210 
4211   PetscFunctionBegin;
4212   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4213   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
4214   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
4215   /* Calculate support sizes */
4216   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4217   for (p = pStart; p < pEnd; ++p) {
4218     PetscInt dof, off, c;
4219 
4220     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4221     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4222     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
4223   }
4224   PetscCall(PetscSectionSetUp(mesh->supportSection));
4225   /* Calculate supports */
4226   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
4227   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
4228   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
4229   for (p = pStart; p < pEnd; ++p) {
4230     PetscInt dof, off, c;
4231 
4232     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4233     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4234     for (c = off; c < off + dof; ++c) {
4235       const PetscInt q = mesh->cones[c];
4236       PetscInt       offS;
4237 
4238       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
4239 
4240       mesh->supports[offS + offsets[q]] = p;
4241       ++offsets[q];
4242     }
4243   }
4244   PetscCall(PetscFree(offsets));
4245   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
4246   PetscFunctionReturn(PETSC_SUCCESS);
4247 }
4248 
4249 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
4250 {
4251   IS stratumIS;
4252 
4253   PetscFunctionBegin;
4254   if (pStart >= pEnd) PetscFunctionReturn(PETSC_SUCCESS);
4255   if (PetscDefined(USE_DEBUG)) {
4256     PetscInt  qStart, qEnd, numLevels, level;
4257     PetscBool overlap = PETSC_FALSE;
4258     PetscCall(DMLabelGetNumValues(label, &numLevels));
4259     for (level = 0; level < numLevels; level++) {
4260       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4261       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
4262         overlap = PETSC_TRUE;
4263         break;
4264       }
4265     }
4266     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);
4267   }
4268   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
4269   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
4270   PetscCall(ISDestroy(&stratumIS));
4271   PetscFunctionReturn(PETSC_SUCCESS);
4272 }
4273 
4274 static PetscErrorCode DMPlexStratify_CellType_Private(DM dm, DMLabel label)
4275 {
4276   PetscInt *pMin, *pMax;
4277   PetscInt  pStart, pEnd;
4278   PetscInt  dmin = PETSC_MAX_INT, dmax = PETSC_MIN_INT;
4279 
4280   PetscFunctionBegin;
4281   {
4282     DMLabel label2;
4283 
4284     PetscCall(DMPlexGetCellTypeLabel(dm, &label2));
4285     PetscCall(PetscObjectViewFromOptions((PetscObject)label2, NULL, "-ct_view"));
4286   }
4287   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4288   for (PetscInt p = pStart; p < pEnd; ++p) {
4289     DMPolytopeType ct;
4290 
4291     PetscCall(DMPlexGetCellType(dm, p, &ct));
4292     dmin = PetscMin(DMPolytopeTypeGetDim(ct), dmin);
4293     dmax = PetscMax(DMPolytopeTypeGetDim(ct), dmax);
4294   }
4295   PetscCall(PetscMalloc2(dmax + 1, &pMin, dmax + 1, &pMax));
4296   for (PetscInt d = dmin; d <= dmax; ++d) {
4297     pMin[d] = PETSC_MAX_INT;
4298     pMax[d] = PETSC_MIN_INT;
4299   }
4300   for (PetscInt p = pStart; p < pEnd; ++p) {
4301     DMPolytopeType ct;
4302     PetscInt       d;
4303 
4304     PetscCall(DMPlexGetCellType(dm, p, &ct));
4305     d       = DMPolytopeTypeGetDim(ct);
4306     pMin[d] = PetscMin(p, pMin[d]);
4307     pMax[d] = PetscMax(p, pMax[d]);
4308   }
4309   for (PetscInt d = dmin; d <= dmax; ++d) {
4310     if (pMin[d] > pMax[d]) continue;
4311     PetscCall(DMPlexCreateDepthStratum(dm, label, d, pMin[d], pMax[d] + 1));
4312   }
4313   PetscCall(PetscFree2(pMin, pMax));
4314   PetscFunctionReturn(PETSC_SUCCESS);
4315 }
4316 
4317 static PetscErrorCode DMPlexStratify_Topological_Private(DM dm, DMLabel label)
4318 {
4319   PetscInt pStart, pEnd;
4320   PetscInt numRoots = 0, numLeaves = 0;
4321 
4322   PetscFunctionBegin;
4323   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4324   {
4325     /* Initialize roots and count leaves */
4326     PetscInt sMin = PETSC_MAX_INT;
4327     PetscInt sMax = PETSC_MIN_INT;
4328     PetscInt coneSize, supportSize;
4329 
4330     for (PetscInt p = pStart; p < pEnd; ++p) {
4331       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4332       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4333       if (!coneSize && supportSize) {
4334         sMin = PetscMin(p, sMin);
4335         sMax = PetscMax(p, sMax);
4336         ++numRoots;
4337       } else if (!supportSize && coneSize) {
4338         ++numLeaves;
4339       } else if (!supportSize && !coneSize) {
4340         /* Isolated points */
4341         sMin = PetscMin(p, sMin);
4342         sMax = PetscMax(p, sMax);
4343       }
4344     }
4345     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4346   }
4347 
4348   if (numRoots + numLeaves == (pEnd - pStart)) {
4349     PetscInt sMin = PETSC_MAX_INT;
4350     PetscInt sMax = PETSC_MIN_INT;
4351     PetscInt coneSize, supportSize;
4352 
4353     for (PetscInt p = pStart; p < pEnd; ++p) {
4354       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4355       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4356       if (!supportSize && coneSize) {
4357         sMin = PetscMin(p, sMin);
4358         sMax = PetscMax(p, sMax);
4359       }
4360     }
4361     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4362   } else {
4363     PetscInt level = 0;
4364     PetscInt qStart, qEnd;
4365 
4366     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4367     while (qEnd > qStart) {
4368       PetscInt sMin = PETSC_MAX_INT;
4369       PetscInt sMax = PETSC_MIN_INT;
4370 
4371       for (PetscInt q = qStart; q < qEnd; ++q) {
4372         const PetscInt *support;
4373         PetscInt        supportSize;
4374 
4375         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4376         PetscCall(DMPlexGetSupport(dm, q, &support));
4377         for (PetscInt s = 0; s < supportSize; ++s) {
4378           sMin = PetscMin(support[s], sMin);
4379           sMax = PetscMax(support[s], sMax);
4380         }
4381       }
4382       PetscCall(DMLabelGetNumValues(label, &level));
4383       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4384       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4385     }
4386   }
4387   PetscFunctionReturn(PETSC_SUCCESS);
4388 }
4389 
4390 /*@
4391   DMPlexStratify - Computes the strata for all points in the `DMPLEX`
4392 
4393   Collective
4394 
4395   Input Parameter:
4396 . dm - The `DMPLEX`
4397 
4398   Level: beginner
4399 
4400   Notes:
4401   The strata group all points of the same grade, and this function calculates the strata. This
4402   grade can be seen as the height (or depth) of the point in the DAG.
4403 
4404   The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
4405   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram).
4406   Concretely, `DMPlexStratify()` creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
4407   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
4408   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through `DMPlexGetDepthLabel()` or `DMPlexGetDepthStratum()`, or
4409   manually via `DMGetLabel()`.  The height is defined implicitly by height = maxDimension - depth, and can be accessed
4410   via `DMPlexGetHeightStratum()`.  For example, cells have height 0 and faces have height 1.
4411 
4412   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
4413   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
4414   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
4415   to interpolate only that one (e0), so that
4416 .vb
4417   cone(c0) = {e0, v2}
4418   cone(e0) = {v0, v1}
4419 .ve
4420   If `DMPlexStratify()` is run on this mesh, it will give depths
4421 .vb
4422    depth 0 = {v0, v1, v2}
4423    depth 1 = {e0, c0}
4424 .ve
4425   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
4426 
4427   `DMPlexStratify()` should be called after all calls to `DMPlexSymmetrize()`
4428 
4429 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
4430 @*/
4431 PetscErrorCode DMPlexStratify(DM dm)
4432 {
4433   DM_Plex  *mesh = (DM_Plex *)dm->data;
4434   DMLabel   label;
4435   PetscBool flg = PETSC_FALSE;
4436 
4437   PetscFunctionBegin;
4438   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4439   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
4440 
4441   // Create depth label
4442   PetscCall(DMRemoveLabel(dm, "depth", NULL));
4443   PetscCall(DMCreateLabel(dm, "depth"));
4444   PetscCall(DMPlexGetDepthLabel(dm, &label));
4445 
4446   PetscCall(PetscOptionsGetBool(NULL, dm->hdr.prefix, "-dm_plex_stratify_celltype", &flg, NULL));
4447   if (flg) PetscCall(DMPlexStratify_CellType_Private(dm, label));
4448   else PetscCall(DMPlexStratify_Topological_Private(dm, label));
4449 
4450   { /* just in case there is an empty process */
4451     PetscInt numValues, maxValues = 0, v;
4452 
4453     PetscCall(DMLabelGetNumValues(label, &numValues));
4454     PetscCall(MPIU_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4455     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4456   }
4457   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4458   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4459   PetscFunctionReturn(PETSC_SUCCESS);
4460 }
4461 
4462 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4463 {
4464   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4465   PetscInt       dim, depth, pheight, coneSize;
4466 
4467   PetscFunctionBeginHot;
4468   PetscCall(DMGetDimension(dm, &dim));
4469   PetscCall(DMPlexGetDepth(dm, &depth));
4470   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4471   pheight = depth - pdepth;
4472   if (depth <= 1) {
4473     switch (pdepth) {
4474     case 0:
4475       ct = DM_POLYTOPE_POINT;
4476       break;
4477     case 1:
4478       switch (coneSize) {
4479       case 2:
4480         ct = DM_POLYTOPE_SEGMENT;
4481         break;
4482       case 3:
4483         ct = DM_POLYTOPE_TRIANGLE;
4484         break;
4485       case 4:
4486         switch (dim) {
4487         case 2:
4488           ct = DM_POLYTOPE_QUADRILATERAL;
4489           break;
4490         case 3:
4491           ct = DM_POLYTOPE_TETRAHEDRON;
4492           break;
4493         default:
4494           break;
4495         }
4496         break;
4497       case 5:
4498         ct = DM_POLYTOPE_PYRAMID;
4499         break;
4500       case 6:
4501         ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4502         break;
4503       case 8:
4504         ct = DM_POLYTOPE_HEXAHEDRON;
4505         break;
4506       default:
4507         break;
4508       }
4509     }
4510   } else {
4511     if (pdepth == 0) {
4512       ct = DM_POLYTOPE_POINT;
4513     } else if (pheight == 0) {
4514       switch (dim) {
4515       case 1:
4516         switch (coneSize) {
4517         case 2:
4518           ct = DM_POLYTOPE_SEGMENT;
4519           break;
4520         default:
4521           break;
4522         }
4523         break;
4524       case 2:
4525         switch (coneSize) {
4526         case 3:
4527           ct = DM_POLYTOPE_TRIANGLE;
4528           break;
4529         case 4:
4530           ct = DM_POLYTOPE_QUADRILATERAL;
4531           break;
4532         default:
4533           break;
4534         }
4535         break;
4536       case 3:
4537         switch (coneSize) {
4538         case 4:
4539           ct = DM_POLYTOPE_TETRAHEDRON;
4540           break;
4541         case 5: {
4542           const PetscInt *cone;
4543           PetscInt        faceConeSize;
4544 
4545           PetscCall(DMPlexGetCone(dm, p, &cone));
4546           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4547           switch (faceConeSize) {
4548           case 3:
4549             ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4550             break;
4551           case 4:
4552             ct = DM_POLYTOPE_PYRAMID;
4553             break;
4554           }
4555         } break;
4556         case 6:
4557           ct = DM_POLYTOPE_HEXAHEDRON;
4558           break;
4559         default:
4560           break;
4561         }
4562         break;
4563       default:
4564         break;
4565       }
4566     } else if (pheight > 0) {
4567       switch (coneSize) {
4568       case 2:
4569         ct = DM_POLYTOPE_SEGMENT;
4570         break;
4571       case 3:
4572         ct = DM_POLYTOPE_TRIANGLE;
4573         break;
4574       case 4:
4575         ct = DM_POLYTOPE_QUADRILATERAL;
4576         break;
4577       default:
4578         break;
4579       }
4580     }
4581   }
4582   *pt = ct;
4583   PetscFunctionReturn(PETSC_SUCCESS);
4584 }
4585 
4586 /*@
4587   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4588 
4589   Collective
4590 
4591   Input Parameter:
4592 . dm - The `DMPLEX`
4593 
4594   Level: developer
4595 
4596   Note:
4597   This function is normally called automatically when a cell type is requested. It creates an
4598   internal `DMLabel` named "celltype" which can be directly accessed using `DMGetLabel()`. A user may disable
4599   automatic creation by creating the label manually, using `DMCreateLabel`(dm, "celltype").
4600 
4601   `DMPlexComputeCellTypes()` should be called after all calls to `DMPlexSymmetrize()` and `DMPlexStratify()`
4602 
4603 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4604 @*/
4605 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4606 {
4607   DM_Plex *mesh;
4608   DMLabel  ctLabel;
4609   PetscInt pStart, pEnd, p;
4610 
4611   PetscFunctionBegin;
4612   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4613   mesh = (DM_Plex *)dm->data;
4614   PetscCall(DMCreateLabel(dm, "celltype"));
4615   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4616   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4617   PetscCall(PetscFree(mesh->cellTypes));
4618   PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
4619   for (p = pStart; p < pEnd; ++p) {
4620     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4621     PetscInt       pdepth;
4622 
4623     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4624     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4625     PetscCheck(ct != DM_POLYTOPE_UNKNOWN && ct != DM_POLYTOPE_UNKNOWN_CELL && ct != DM_POLYTOPE_UNKNOWN_FACE, PETSC_COMM_SELF, PETSC_ERR_SUP, "Point %" PetscInt_FMT " is screwed up", p);
4626     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4627     mesh->cellTypes[p - pStart].value_as_uint8 = ct;
4628   }
4629   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4630   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4631   PetscFunctionReturn(PETSC_SUCCESS);
4632 }
4633 
4634 /*@C
4635   DMPlexGetJoin - Get an array for the join of the set of points
4636 
4637   Not Collective
4638 
4639   Input Parameters:
4640 + dm        - The `DMPLEX` object
4641 . numPoints - The number of input points for the join
4642 - points    - The input points
4643 
4644   Output Parameters:
4645 + numCoveredPoints - The number of points in the join
4646 - coveredPoints    - The points in the join
4647 
4648   Level: intermediate
4649 
4650   Note:
4651   Currently, this is restricted to a single level join
4652 
4653   Fortran Notes:
4654   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4655 
4656 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4657 @*/
4658 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4659 {
4660   DM_Plex  *mesh = (DM_Plex *)dm->data;
4661   PetscInt *join[2];
4662   PetscInt  joinSize, i = 0;
4663   PetscInt  dof, off, p, c, m;
4664   PetscInt  maxSupportSize;
4665 
4666   PetscFunctionBegin;
4667   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4668   PetscAssertPointer(points, 3);
4669   PetscAssertPointer(numCoveredPoints, 4);
4670   PetscAssertPointer(coveredPoints, 5);
4671   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4672   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4673   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4674   /* Copy in support of first point */
4675   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4676   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4677   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4678   /* Check each successive support */
4679   for (p = 1; p < numPoints; ++p) {
4680     PetscInt newJoinSize = 0;
4681 
4682     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4683     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4684     for (c = 0; c < dof; ++c) {
4685       const PetscInt point = mesh->supports[off + c];
4686 
4687       for (m = 0; m < joinSize; ++m) {
4688         if (point == join[i][m]) {
4689           join[1 - i][newJoinSize++] = point;
4690           break;
4691         }
4692       }
4693     }
4694     joinSize = newJoinSize;
4695     i        = 1 - i;
4696   }
4697   *numCoveredPoints = joinSize;
4698   *coveredPoints    = join[i];
4699   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4700   PetscFunctionReturn(PETSC_SUCCESS);
4701 }
4702 
4703 /*@C
4704   DMPlexRestoreJoin - Restore an array for the join of the set of points
4705 
4706   Not Collective
4707 
4708   Input Parameters:
4709 + dm        - The `DMPLEX` object
4710 . numPoints - The number of input points for the join
4711 - points    - The input points
4712 
4713   Output Parameters:
4714 + numCoveredPoints - The number of points in the join
4715 - coveredPoints    - The points in the join
4716 
4717   Level: intermediate
4718 
4719   Fortran Notes:
4720   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4721 
4722 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4723 @*/
4724 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4725 {
4726   PetscFunctionBegin;
4727   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4728   if (points) PetscAssertPointer(points, 3);
4729   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4730   PetscAssertPointer(coveredPoints, 5);
4731   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4732   if (numCoveredPoints) *numCoveredPoints = 0;
4733   PetscFunctionReturn(PETSC_SUCCESS);
4734 }
4735 
4736 /*@C
4737   DMPlexGetFullJoin - Get an array for the join of the set of points
4738 
4739   Not Collective
4740 
4741   Input Parameters:
4742 + dm        - The `DMPLEX` object
4743 . numPoints - The number of input points for the join
4744 - points    - The input points
4745 
4746   Output Parameters:
4747 + numCoveredPoints - The number of points in the join
4748 - coveredPoints    - The points in the join
4749 
4750   Level: intermediate
4751 
4752   Fortran Notes:
4753   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4754 
4755 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4756 @*/
4757 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4758 {
4759   PetscInt *offsets, **closures;
4760   PetscInt *join[2];
4761   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4762   PetscInt  p, d, c, m, ms;
4763 
4764   PetscFunctionBegin;
4765   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4766   PetscAssertPointer(points, 3);
4767   PetscAssertPointer(numCoveredPoints, 4);
4768   PetscAssertPointer(coveredPoints, 5);
4769 
4770   PetscCall(DMPlexGetDepth(dm, &depth));
4771   PetscCall(PetscCalloc1(numPoints, &closures));
4772   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4773   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4774   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4775   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4776   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4777 
4778   for (p = 0; p < numPoints; ++p) {
4779     PetscInt closureSize;
4780 
4781     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4782 
4783     offsets[p * (depth + 2) + 0] = 0;
4784     for (d = 0; d < depth + 1; ++d) {
4785       PetscInt pStart, pEnd, i;
4786 
4787       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4788       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4789         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4790           offsets[p * (depth + 2) + d + 1] = i;
4791           break;
4792         }
4793       }
4794       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4795     }
4796     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);
4797   }
4798   for (d = 0; d < depth + 1; ++d) {
4799     PetscInt dof;
4800 
4801     /* Copy in support of first point */
4802     dof = offsets[d + 1] - offsets[d];
4803     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4804     /* Check each successive cone */
4805     for (p = 1; p < numPoints && joinSize; ++p) {
4806       PetscInt newJoinSize = 0;
4807 
4808       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
4809       for (c = 0; c < dof; ++c) {
4810         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
4811 
4812         for (m = 0; m < joinSize; ++m) {
4813           if (point == join[i][m]) {
4814             join[1 - i][newJoinSize++] = point;
4815             break;
4816           }
4817         }
4818       }
4819       joinSize = newJoinSize;
4820       i        = 1 - i;
4821     }
4822     if (joinSize) break;
4823   }
4824   *numCoveredPoints = joinSize;
4825   *coveredPoints    = join[i];
4826   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4827   PetscCall(PetscFree(closures));
4828   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4829   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
4830   PetscFunctionReturn(PETSC_SUCCESS);
4831 }
4832 
4833 /*@C
4834   DMPlexGetMeet - Get an array for the meet of the set of points
4835 
4836   Not Collective
4837 
4838   Input Parameters:
4839 + dm        - The `DMPLEX` object
4840 . numPoints - The number of input points for the meet
4841 - points    - The input points
4842 
4843   Output Parameters:
4844 + numCoveringPoints - The number of points in the meet
4845 - coveringPoints    - The points in the meet
4846 
4847   Level: intermediate
4848 
4849   Note:
4850   Currently, this is restricted to a single level meet
4851 
4852   Fortran Notes:
4853   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4854 
4855 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4856 @*/
4857 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt **coveringPoints)
4858 {
4859   DM_Plex  *mesh = (DM_Plex *)dm->data;
4860   PetscInt *meet[2];
4861   PetscInt  meetSize, i = 0;
4862   PetscInt  dof, off, p, c, m;
4863   PetscInt  maxConeSize;
4864 
4865   PetscFunctionBegin;
4866   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4867   PetscAssertPointer(points, 3);
4868   PetscAssertPointer(numCoveringPoints, 4);
4869   PetscAssertPointer(coveringPoints, 5);
4870   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4871   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4872   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4873   /* Copy in cone of first point */
4874   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4875   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4876   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
4877   /* Check each successive cone */
4878   for (p = 1; p < numPoints; ++p) {
4879     PetscInt newMeetSize = 0;
4880 
4881     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4882     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4883     for (c = 0; c < dof; ++c) {
4884       const PetscInt point = mesh->cones[off + c];
4885 
4886       for (m = 0; m < meetSize; ++m) {
4887         if (point == meet[i][m]) {
4888           meet[1 - i][newMeetSize++] = point;
4889           break;
4890         }
4891       }
4892     }
4893     meetSize = newMeetSize;
4894     i        = 1 - i;
4895   }
4896   *numCoveringPoints = meetSize;
4897   *coveringPoints    = meet[i];
4898   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
4899   PetscFunctionReturn(PETSC_SUCCESS);
4900 }
4901 
4902 /*@C
4903   DMPlexRestoreMeet - Restore an array for the meet of the set of points
4904 
4905   Not Collective
4906 
4907   Input Parameters:
4908 + dm        - The `DMPLEX` object
4909 . numPoints - The number of input points for the meet
4910 - points    - The input points
4911 
4912   Output Parameters:
4913 + numCoveredPoints - The number of points in the meet
4914 - coveredPoints    - The points in the meet
4915 
4916   Level: intermediate
4917 
4918   Fortran Notes:
4919   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4920 
4921 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
4922 @*/
4923 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4924 {
4925   PetscFunctionBegin;
4926   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4927   if (points) PetscAssertPointer(points, 3);
4928   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4929   PetscAssertPointer(coveredPoints, 5);
4930   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4931   if (numCoveredPoints) *numCoveredPoints = 0;
4932   PetscFunctionReturn(PETSC_SUCCESS);
4933 }
4934 
4935 /*@C
4936   DMPlexGetFullMeet - Get an array for the meet of the set of points
4937 
4938   Not Collective
4939 
4940   Input Parameters:
4941 + dm        - The `DMPLEX` object
4942 . numPoints - The number of input points for the meet
4943 - points    - The input points
4944 
4945   Output Parameters:
4946 + numCoveredPoints - The number of points in the meet
4947 - coveredPoints    - The points in the meet
4948 
4949   Level: intermediate
4950 
4951   Fortran Notes:
4952   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4953 
4954 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4955 @*/
4956 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4957 {
4958   PetscInt *offsets, **closures;
4959   PetscInt *meet[2];
4960   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
4961   PetscInt  p, h, c, m, mc;
4962 
4963   PetscFunctionBegin;
4964   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4965   PetscAssertPointer(points, 3);
4966   PetscAssertPointer(numCoveredPoints, 4);
4967   PetscAssertPointer(coveredPoints, 5);
4968 
4969   PetscCall(DMPlexGetDepth(dm, &height));
4970   PetscCall(PetscMalloc1(numPoints, &closures));
4971   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4972   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
4973   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
4974   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
4975   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
4976 
4977   for (p = 0; p < numPoints; ++p) {
4978     PetscInt closureSize;
4979 
4980     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
4981 
4982     offsets[p * (height + 2) + 0] = 0;
4983     for (h = 0; h < height + 1; ++h) {
4984       PetscInt pStart, pEnd, i;
4985 
4986       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
4987       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
4988         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4989           offsets[p * (height + 2) + h + 1] = i;
4990           break;
4991         }
4992       }
4993       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
4994     }
4995     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);
4996   }
4997   for (h = 0; h < height + 1; ++h) {
4998     PetscInt dof;
4999 
5000     /* Copy in cone of first point */
5001     dof = offsets[h + 1] - offsets[h];
5002     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
5003     /* Check each successive cone */
5004     for (p = 1; p < numPoints && meetSize; ++p) {
5005       PetscInt newMeetSize = 0;
5006 
5007       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
5008       for (c = 0; c < dof; ++c) {
5009         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
5010 
5011         for (m = 0; m < meetSize; ++m) {
5012           if (point == meet[i][m]) {
5013             meet[1 - i][newMeetSize++] = point;
5014             break;
5015           }
5016         }
5017       }
5018       meetSize = newMeetSize;
5019       i        = 1 - i;
5020     }
5021     if (meetSize) break;
5022   }
5023   *numCoveredPoints = meetSize;
5024   *coveredPoints    = meet[i];
5025   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
5026   PetscCall(PetscFree(closures));
5027   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5028   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
5029   PetscFunctionReturn(PETSC_SUCCESS);
5030 }
5031 
5032 /*@C
5033   DMPlexEqual - Determine if two `DM` have the same topology
5034 
5035   Not Collective
5036 
5037   Input Parameters:
5038 + dmA - A `DMPLEX` object
5039 - dmB - A `DMPLEX` object
5040 
5041   Output Parameter:
5042 . equal - `PETSC_TRUE` if the topologies are identical
5043 
5044   Level: intermediate
5045 
5046   Note:
5047   We are not solving graph isomorphism, so we do not permute.
5048 
5049 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5050 @*/
5051 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
5052 {
5053   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
5054 
5055   PetscFunctionBegin;
5056   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
5057   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
5058   PetscAssertPointer(equal, 3);
5059 
5060   *equal = PETSC_FALSE;
5061   PetscCall(DMPlexGetDepth(dmA, &depth));
5062   PetscCall(DMPlexGetDepth(dmB, &depthB));
5063   if (depth != depthB) PetscFunctionReturn(PETSC_SUCCESS);
5064   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
5065   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
5066   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(PETSC_SUCCESS);
5067   for (p = pStart; p < pEnd; ++p) {
5068     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
5069     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
5070 
5071     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
5072     PetscCall(DMPlexGetCone(dmA, p, &cone));
5073     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
5074     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
5075     PetscCall(DMPlexGetCone(dmB, p, &coneB));
5076     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
5077     if (coneSize != coneSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5078     for (c = 0; c < coneSize; ++c) {
5079       if (cone[c] != coneB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5080       if (ornt[c] != orntB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5081     }
5082     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
5083     PetscCall(DMPlexGetSupport(dmA, p, &support));
5084     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
5085     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
5086     if (supportSize != supportSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5087     for (s = 0; s < supportSize; ++s) {
5088       if (support[s] != supportB[s]) PetscFunctionReturn(PETSC_SUCCESS);
5089     }
5090   }
5091   *equal = PETSC_TRUE;
5092   PetscFunctionReturn(PETSC_SUCCESS);
5093 }
5094 
5095 /*@C
5096   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
5097 
5098   Not Collective
5099 
5100   Input Parameters:
5101 + dm         - The `DMPLEX`
5102 . cellDim    - The cell dimension
5103 - numCorners - The number of vertices on a cell
5104 
5105   Output Parameter:
5106 . numFaceVertices - The number of vertices on a face
5107 
5108   Level: developer
5109 
5110   Note:
5111   Of course this can only work for a restricted set of symmetric shapes
5112 
5113 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5114 @*/
5115 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
5116 {
5117   MPI_Comm comm;
5118 
5119   PetscFunctionBegin;
5120   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
5121   PetscAssertPointer(numFaceVertices, 4);
5122   switch (cellDim) {
5123   case 0:
5124     *numFaceVertices = 0;
5125     break;
5126   case 1:
5127     *numFaceVertices = 1;
5128     break;
5129   case 2:
5130     switch (numCorners) {
5131     case 3:                 /* triangle */
5132       *numFaceVertices = 2; /* Edge has 2 vertices */
5133       break;
5134     case 4:                 /* quadrilateral */
5135       *numFaceVertices = 2; /* Edge has 2 vertices */
5136       break;
5137     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
5138       *numFaceVertices = 3; /* Edge has 3 vertices */
5139       break;
5140     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
5141       *numFaceVertices = 3; /* Edge has 3 vertices */
5142       break;
5143     default:
5144       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5145     }
5146     break;
5147   case 3:
5148     switch (numCorners) {
5149     case 4:                 /* tetradehdron */
5150       *numFaceVertices = 3; /* Face has 3 vertices */
5151       break;
5152     case 6:                 /* tet cohesive cells */
5153       *numFaceVertices = 4; /* Face has 4 vertices */
5154       break;
5155     case 8:                 /* hexahedron */
5156       *numFaceVertices = 4; /* Face has 4 vertices */
5157       break;
5158     case 9:                 /* tet cohesive Lagrange cells */
5159       *numFaceVertices = 6; /* Face has 6 vertices */
5160       break;
5161     case 10:                /* quadratic tetrahedron */
5162       *numFaceVertices = 6; /* Face has 6 vertices */
5163       break;
5164     case 12:                /* hex cohesive Lagrange cells */
5165       *numFaceVertices = 6; /* Face has 6 vertices */
5166       break;
5167     case 18:                /* quadratic tet cohesive Lagrange cells */
5168       *numFaceVertices = 6; /* Face has 6 vertices */
5169       break;
5170     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
5171       *numFaceVertices = 9; /* Face has 9 vertices */
5172       break;
5173     default:
5174       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5175     }
5176     break;
5177   default:
5178     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
5179   }
5180   PetscFunctionReturn(PETSC_SUCCESS);
5181 }
5182 
5183 /*@
5184   DMPlexGetDepthLabel - Get the `DMLabel` recording the depth of each point
5185 
5186   Not Collective
5187 
5188   Input Parameter:
5189 . dm - The `DMPLEX` object
5190 
5191   Output Parameter:
5192 . depthLabel - The `DMLabel` recording point depth
5193 
5194   Level: developer
5195 
5196 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
5197 @*/
5198 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
5199 {
5200   PetscFunctionBegin;
5201   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5202   PetscAssertPointer(depthLabel, 2);
5203   *depthLabel = dm->depthLabel;
5204   PetscFunctionReturn(PETSC_SUCCESS);
5205 }
5206 
5207 /*@
5208   DMPlexGetDepth - Get the depth of the DAG representing this mesh
5209 
5210   Not Collective
5211 
5212   Input Parameter:
5213 . dm - The `DMPLEX` object
5214 
5215   Output Parameter:
5216 . depth - The number of strata (breadth first levels) in the DAG
5217 
5218   Level: developer
5219 
5220   Notes:
5221   This returns maximum of point depths over all points, i.e. maximum value of the label returned by `DMPlexGetDepthLabel()`.
5222 
5223   The point depth is described more in detail in `DMPlexGetDepthStratum()`.
5224 
5225   An empty mesh gives -1.
5226 
5227 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
5228 @*/
5229 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
5230 {
5231   DM_Plex *mesh = (DM_Plex *)dm->data;
5232   DMLabel  label;
5233   PetscInt d = 0;
5234 
5235   PetscFunctionBegin;
5236   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5237   PetscAssertPointer(depth, 2);
5238   if (mesh->tr) {
5239     PetscCall(DMPlexTransformGetDepth(mesh->tr, depth));
5240   } else {
5241     PetscCall(DMPlexGetDepthLabel(dm, &label));
5242     if (label) PetscCall(DMLabelGetNumValues(label, &d));
5243     *depth = d - 1;
5244   }
5245   PetscFunctionReturn(PETSC_SUCCESS);
5246 }
5247 
5248 /*@
5249   DMPlexGetDepthStratum - Get the bounds [`start`, `end`) for all points at a certain depth.
5250 
5251   Not Collective
5252 
5253   Input Parameters:
5254 + dm    - The `DMPLEX` object
5255 - depth - The requested depth
5256 
5257   Output Parameters:
5258 + start - The first point at this `depth`
5259 - end   - One beyond the last point at this `depth`
5260 
5261   Level: developer
5262 
5263   Notes:
5264   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
5265   often "vertices".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then depth stratum 1 contains the next
5266   higher dimension, e.g., "edges".
5267 
5268 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetHeightStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
5269 @*/
5270 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
5271 {
5272   DM_Plex *mesh = (DM_Plex *)dm->data;
5273   DMLabel  label;
5274   PetscInt pStart, pEnd;
5275 
5276   PetscFunctionBegin;
5277   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5278   if (start) {
5279     PetscAssertPointer(start, 3);
5280     *start = 0;
5281   }
5282   if (end) {
5283     PetscAssertPointer(end, 4);
5284     *end = 0;
5285   }
5286   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5287   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5288   if (depth < 0) {
5289     if (start) *start = pStart;
5290     if (end) *end = pEnd;
5291     PetscFunctionReturn(PETSC_SUCCESS);
5292   }
5293   if (mesh->tr) {
5294     PetscCall(DMPlexTransformGetDepthStratum(mesh->tr, depth, start, end));
5295   } else {
5296     PetscCall(DMPlexGetDepthLabel(dm, &label));
5297     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5298     PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
5299   }
5300   PetscFunctionReturn(PETSC_SUCCESS);
5301 }
5302 
5303 /*@
5304   DMPlexGetHeightStratum - Get the bounds [`start`, `end`) for all points at a certain height.
5305 
5306   Not Collective
5307 
5308   Input Parameters:
5309 + dm     - The `DMPLEX` object
5310 - height - The requested height
5311 
5312   Output Parameters:
5313 + start - The first point at this `height`
5314 - end   - One beyond the last point at this `height`
5315 
5316   Level: developer
5317 
5318   Notes:
5319   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
5320   points, often called "cells" or "elements".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then height
5321   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
5322 
5323 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5324 @*/
5325 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
5326 {
5327   DMLabel  label;
5328   PetscInt depth, pStart, pEnd;
5329 
5330   PetscFunctionBegin;
5331   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5332   if (start) {
5333     PetscAssertPointer(start, 3);
5334     *start = 0;
5335   }
5336   if (end) {
5337     PetscAssertPointer(end, 4);
5338     *end = 0;
5339   }
5340   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5341   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5342   if (height < 0) {
5343     if (start) *start = pStart;
5344     if (end) *end = pEnd;
5345     PetscFunctionReturn(PETSC_SUCCESS);
5346   }
5347   PetscCall(DMPlexGetDepthLabel(dm, &label));
5348   if (label) PetscCall(DMLabelGetNumValues(label, &depth));
5349   else PetscCall(DMGetDimension(dm, &depth));
5350   PetscCheck(depth >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Depth not yet computed");
5351   PetscCall(DMPlexGetDepthStratum(dm, depth - 1 - height, start, end));
5352   PetscFunctionReturn(PETSC_SUCCESS);
5353 }
5354 
5355 /*@
5356   DMPlexGetPointDepth - Get the `depth` of a given point
5357 
5358   Not Collective
5359 
5360   Input Parameters:
5361 + dm    - The `DMPLEX` object
5362 - point - The point
5363 
5364   Output Parameter:
5365 . depth - The depth of the `point`
5366 
5367   Level: intermediate
5368 
5369 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5370 @*/
5371 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
5372 {
5373   PetscFunctionBegin;
5374   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5375   PetscAssertPointer(depth, 3);
5376   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
5377   PetscFunctionReturn(PETSC_SUCCESS);
5378 }
5379 
5380 /*@
5381   DMPlexGetPointHeight - Get the `height` of a given point
5382 
5383   Not Collective
5384 
5385   Input Parameters:
5386 + dm    - The `DMPLEX` object
5387 - point - The point
5388 
5389   Output Parameter:
5390 . height - The height of the `point`
5391 
5392   Level: intermediate
5393 
5394 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
5395 @*/
5396 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5397 {
5398   PetscInt n, pDepth;
5399 
5400   PetscFunctionBegin;
5401   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5402   PetscAssertPointer(height, 3);
5403   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5404   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5405   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5406   PetscFunctionReturn(PETSC_SUCCESS);
5407 }
5408 
5409 /*@
5410   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5411 
5412   Not Collective
5413 
5414   Input Parameter:
5415 . dm - The `DMPLEX` object
5416 
5417   Output Parameter:
5418 . celltypeLabel - The `DMLabel` recording cell polytope type
5419 
5420   Level: developer
5421 
5422   Note:
5423   This function will trigger automatica computation of cell types. This can be disabled by calling
5424   `DMCreateLabel`(dm, "celltype") beforehand.
5425 
5426 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5427 @*/
5428 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5429 {
5430   PetscFunctionBegin;
5431   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5432   PetscAssertPointer(celltypeLabel, 2);
5433   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5434   *celltypeLabel = dm->celltypeLabel;
5435   PetscFunctionReturn(PETSC_SUCCESS);
5436 }
5437 
5438 /*@
5439   DMPlexGetCellType - Get the polytope type of a given cell
5440 
5441   Not Collective
5442 
5443   Input Parameters:
5444 + dm   - The `DMPLEX` object
5445 - cell - The cell
5446 
5447   Output Parameter:
5448 . celltype - The polytope type of the cell
5449 
5450   Level: intermediate
5451 
5452 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeType`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5453 @*/
5454 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5455 {
5456   DM_Plex *mesh = (DM_Plex *)dm->data;
5457   DMLabel  label;
5458   PetscInt ct;
5459 
5460   PetscFunctionBegin;
5461   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5462   PetscAssertPointer(celltype, 3);
5463   if (mesh->tr) {
5464     PetscCall(DMPlexTransformGetCellType(mesh->tr, cell, celltype));
5465   } else {
5466     PetscInt pStart, pEnd;
5467 
5468     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, NULL));
5469     if (!mesh->cellTypes) { /* XXX remove? optimize? */
5470       PetscCall(PetscSectionGetChart(mesh->coneSection, NULL, &pEnd));
5471       PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5472       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5473       for (PetscInt p = pStart; p < pEnd; p++) {
5474         PetscCall(DMLabelGetValue(label, p, &ct));
5475         mesh->cellTypes[p - pStart].value_as_uint8 = (DMPolytopeType)ct;
5476       }
5477     }
5478     *celltype = (DMPolytopeType)mesh->cellTypes[cell - pStart].value_as_uint8;
5479     if (PetscDefined(USE_DEBUG)) {
5480       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5481       PetscCall(DMLabelGetValue(label, cell, &ct));
5482       PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5483       PetscCheck(ct == (PetscInt)*celltype, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid cellType for %" PetscInt_FMT ": %d != %" PetscInt_FMT, cell, (int)*celltype, ct);
5484     }
5485   }
5486   PetscFunctionReturn(PETSC_SUCCESS);
5487 }
5488 
5489 /*@
5490   DMPlexSetCellType - Set the polytope type of a given cell
5491 
5492   Not Collective
5493 
5494   Input Parameters:
5495 + dm       - The `DMPLEX` object
5496 . cell     - The cell
5497 - celltype - The polytope type of the cell
5498 
5499   Level: advanced
5500 
5501   Note:
5502   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5503   is executed. This function will override the computed type. However, if automatic classification will not succeed
5504   and a user wants to manually specify all types, the classification must be disabled by calling
5505   DMCreateLabel(dm, "celltype") before getting or setting any cell types.
5506 
5507 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5508 @*/
5509 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5510 {
5511   DM_Plex *mesh = (DM_Plex *)dm->data;
5512   DMLabel  label;
5513   PetscInt pStart, pEnd;
5514 
5515   PetscFunctionBegin;
5516   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5517   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
5518   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5519   PetscCall(DMLabelSetValue(label, cell, celltype));
5520   if (!mesh->cellTypes) PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5521   mesh->cellTypes[cell - pStart].value_as_uint8 = celltype;
5522   PetscFunctionReturn(PETSC_SUCCESS);
5523 }
5524 
5525 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5526 {
5527   PetscSection section, s;
5528   Mat          m;
5529   PetscInt     maxHeight;
5530   const char  *prefix;
5531 
5532   PetscFunctionBegin;
5533   PetscCall(DMClone(dm, cdm));
5534   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5535   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5536   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5537   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5538   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5539   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5540   PetscCall(DMSetLocalSection(*cdm, section));
5541   PetscCall(PetscSectionDestroy(&section));
5542   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &s));
5543   PetscCall(MatCreate(PETSC_COMM_SELF, &m));
5544   PetscCall(DMSetDefaultConstraints(*cdm, s, m, NULL));
5545   PetscCall(PetscSectionDestroy(&s));
5546   PetscCall(MatDestroy(&m));
5547 
5548   PetscCall(DMSetNumFields(*cdm, 1));
5549   PetscCall(DMCreateDS(*cdm));
5550   (*cdm)->cloneOpts = PETSC_TRUE;
5551   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5552   PetscFunctionReturn(PETSC_SUCCESS);
5553 }
5554 
5555 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5556 {
5557   Vec coordsLocal, cellCoordsLocal;
5558   DM  coordsDM, cellCoordsDM;
5559 
5560   PetscFunctionBegin;
5561   *field = NULL;
5562   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5563   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5564   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5565   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5566   if (coordsLocal && coordsDM) {
5567     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5568     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5569   }
5570   PetscFunctionReturn(PETSC_SUCCESS);
5571 }
5572 
5573 /*@C
5574   DMPlexGetConeSection - Return a section which describes the layout of cone data
5575 
5576   Not Collective
5577 
5578   Input Parameter:
5579 . dm - The `DMPLEX` object
5580 
5581   Output Parameter:
5582 . section - The `PetscSection` object
5583 
5584   Level: developer
5585 
5586 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5587 @*/
5588 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5589 {
5590   DM_Plex *mesh = (DM_Plex *)dm->data;
5591 
5592   PetscFunctionBegin;
5593   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5594   if (section) *section = mesh->coneSection;
5595   PetscFunctionReturn(PETSC_SUCCESS);
5596 }
5597 
5598 /*@C
5599   DMPlexGetSupportSection - Return a section which describes the layout of support data
5600 
5601   Not Collective
5602 
5603   Input Parameter:
5604 . dm - The `DMPLEX` object
5605 
5606   Output Parameter:
5607 . section - The `PetscSection` object
5608 
5609   Level: developer
5610 
5611 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5612 @*/
5613 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5614 {
5615   DM_Plex *mesh = (DM_Plex *)dm->data;
5616 
5617   PetscFunctionBegin;
5618   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5619   if (section) *section = mesh->supportSection;
5620   PetscFunctionReturn(PETSC_SUCCESS);
5621 }
5622 
5623 /*@C
5624   DMPlexGetCones - Return cone data
5625 
5626   Not Collective
5627 
5628   Input Parameter:
5629 . dm - The `DMPLEX` object
5630 
5631   Output Parameter:
5632 . cones - The cone for each point
5633 
5634   Level: developer
5635 
5636 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5637 @*/
5638 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5639 {
5640   DM_Plex *mesh = (DM_Plex *)dm->data;
5641 
5642   PetscFunctionBegin;
5643   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5644   if (cones) *cones = mesh->cones;
5645   PetscFunctionReturn(PETSC_SUCCESS);
5646 }
5647 
5648 /*@C
5649   DMPlexGetConeOrientations - Return cone orientation data
5650 
5651   Not Collective
5652 
5653   Input Parameter:
5654 . dm - The `DMPLEX` object
5655 
5656   Output Parameter:
5657 . coneOrientations - The array of cone orientations for all points
5658 
5659   Level: developer
5660 
5661   Notes:
5662   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points as returned by `DMPlexGetConeOrientation()`.
5663 
5664   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5665 
5666 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5667 @*/
5668 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5669 {
5670   DM_Plex *mesh = (DM_Plex *)dm->data;
5671 
5672   PetscFunctionBegin;
5673   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5674   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5675   PetscFunctionReturn(PETSC_SUCCESS);
5676 }
5677 
5678 /******************************** FEM Support **********************************/
5679 
5680 PetscErrorCode DMPlexGetAllCells_Internal(DM plex, IS *cellIS)
5681 {
5682   PetscInt depth;
5683 
5684   PetscFunctionBegin;
5685   PetscCall(DMPlexGetDepth(plex, &depth));
5686   PetscCall(DMGetStratumIS(plex, "dim", depth, cellIS));
5687   if (!*cellIS) PetscCall(DMGetStratumIS(plex, "depth", depth, cellIS));
5688   PetscFunctionReturn(PETSC_SUCCESS);
5689 }
5690 
5691 PetscErrorCode DMPlexGetAllFaces_Internal(DM plex, IS *faceIS)
5692 {
5693   PetscInt depth;
5694 
5695   PetscFunctionBegin;
5696   PetscCall(DMPlexGetDepth(plex, &depth));
5697   PetscCall(DMGetStratumIS(plex, "dim", depth - 1, faceIS));
5698   if (!*faceIS) PetscCall(DMGetStratumIS(plex, "depth", depth - 1, faceIS));
5699   PetscFunctionReturn(PETSC_SUCCESS);
5700 }
5701 
5702 /*
5703  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5704  representing a line in the section.
5705 */
5706 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(DM dm, PetscSection section, PetscInt field, PetscInt line, PetscInt *Nc, PetscInt *k, PetscBool *continuous, PetscBool *tensor)
5707 {
5708   PetscObject  obj;
5709   PetscClassId id;
5710   PetscFE      fe = NULL;
5711 
5712   PetscFunctionBeginHot;
5713   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5714   PetscCall(DMGetField(dm, field, NULL, &obj));
5715   PetscCall(PetscObjectGetClassId(obj, &id));
5716   if (id == PETSCFE_CLASSID) fe = (PetscFE)obj;
5717 
5718   if (!fe) {
5719     /* Assume the full interpolated mesh is in the chart; lines in particular */
5720     /* An order k SEM disc has k-1 dofs on an edge */
5721     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5722     *k = *k / *Nc + 1;
5723   } else {
5724     PetscInt       dual_space_size, dim;
5725     PetscDualSpace dsp;
5726 
5727     PetscCall(DMGetDimension(dm, &dim));
5728     PetscCall(PetscFEGetDualSpace(fe, &dsp));
5729     PetscCall(PetscDualSpaceGetDimension(dsp, &dual_space_size));
5730     *k = (PetscInt)PetscCeilReal(PetscPowReal(dual_space_size / *Nc, 1.0 / dim)) - 1;
5731     PetscCall(PetscDualSpaceLagrangeGetContinuity(dsp, continuous));
5732     PetscCall(PetscDualSpaceLagrangeGetTensor(dsp, tensor));
5733   }
5734   PetscFunctionReturn(PETSC_SUCCESS);
5735 }
5736 
5737 static PetscErrorCode GetFieldSize_Private(PetscInt dim, PetscInt k, PetscBool tensor, PetscInt *dof)
5738 {
5739   PetscFunctionBeginHot;
5740   if (tensor) {
5741     *dof = PetscPowInt(k + 1, dim);
5742   } else {
5743     switch (dim) {
5744     case 1:
5745       *dof = k + 1;
5746       break;
5747     case 2:
5748       *dof = ((k + 1) * (k + 2)) / 2;
5749       break;
5750     case 3:
5751       *dof = ((k + 1) * (k + 2) * (k + 3)) / 6;
5752       break;
5753     default:
5754       *dof = 0;
5755     }
5756   }
5757   PetscFunctionReturn(PETSC_SUCCESS);
5758 }
5759 
5760 /*@
5761 
5762   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5763   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5764   section provided (or the section of the `DM`).
5765 
5766   Input Parameters:
5767 + dm      - The `DM`
5768 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or `PETSC_DETERMINE`
5769 - section - The `PetscSection` to reorder, or `NULL` for the default section
5770 
5771   Example:
5772   A typical interpolated single-quad mesh might order points as
5773 .vb
5774   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5775 
5776   v4 -- e6 -- v3
5777   |           |
5778   e7    c0    e8
5779   |           |
5780   v1 -- e5 -- v2
5781 .ve
5782 
5783   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5784   dofs in the order of points, e.g.,
5785 .vb
5786     c0 -> [0,1,2,3]
5787     v1 -> [4]
5788     ...
5789     e5 -> [8, 9]
5790 .ve
5791 
5792   which corresponds to the dofs
5793 .vb
5794     6   10  11  7
5795     13  2   3   15
5796     12  0   1   14
5797     4   8   9   5
5798 .ve
5799 
5800   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5801 .vb
5802   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5803 .ve
5804 
5805   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5806 .vb
5807    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5808 .ve
5809 
5810   Level: developer
5811 
5812   Notes:
5813   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5814   degree of the basis.
5815 
5816   This is required to run with libCEED.
5817 
5818 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5819 @*/
5820 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5821 {
5822   DMLabel   label;
5823   PetscInt  dim, depth = -1, eStart = -1, Nf;
5824   PetscBool continuous = PETSC_TRUE, tensor = PETSC_TRUE;
5825 
5826   PetscFunctionBegin;
5827   PetscCall(DMGetDimension(dm, &dim));
5828   if (dim < 1) PetscFunctionReturn(PETSC_SUCCESS);
5829   if (point < 0) {
5830     PetscInt sStart, sEnd;
5831 
5832     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5833     point = sEnd - sStart ? sStart : point;
5834   }
5835   PetscCall(DMPlexGetDepthLabel(dm, &label));
5836   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5837   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5838   if (depth == 1) {
5839     eStart = point;
5840   } else if (depth == dim) {
5841     const PetscInt *cone;
5842 
5843     PetscCall(DMPlexGetCone(dm, point, &cone));
5844     if (dim == 2) eStart = cone[0];
5845     else if (dim == 3) {
5846       const PetscInt *cone2;
5847       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5848       eStart = cone2[0];
5849     } 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);
5850   } 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);
5851 
5852   PetscCall(PetscSectionGetNumFields(section, &Nf));
5853   for (PetscInt d = 1; d <= dim; d++) {
5854     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5855     PetscInt *perm;
5856 
5857     for (f = 0; f < Nf; ++f) {
5858       PetscInt dof;
5859 
5860       PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5861       PetscCheck(dim == 1 || tensor || !continuous, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Continuous field %" PetscInt_FMT " must have a tensor product discretization", f);
5862       if (!continuous && d < dim) continue;
5863       PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
5864       size += dof * Nc;
5865     }
5866     PetscCall(PetscMalloc1(size, &perm));
5867     for (f = 0; f < Nf; ++f) {
5868       switch (d) {
5869       case 1:
5870         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5871         if (!continuous && d < dim) continue;
5872         /*
5873          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5874          We want              [ vtx0; edge of length k-1; vtx1 ]
5875          */
5876         if (continuous) {
5877           for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
5878           for (i = 0; i < k - 1; i++)
5879             for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
5880           for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
5881           foffset = offset;
5882         } else {
5883           PetscInt dof;
5884 
5885           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
5886           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
5887           foffset = offset;
5888         }
5889         break;
5890       case 2:
5891         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5892         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5893         if (!continuous && d < dim) continue;
5894         /* The SEM order is
5895 
5896          v_lb, {e_b}, v_rb,
5897          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5898          v_lt, reverse {e_t}, v_rt
5899          */
5900         if (continuous) {
5901           const PetscInt of   = 0;
5902           const PetscInt oeb  = of + PetscSqr(k - 1);
5903           const PetscInt oer  = oeb + (k - 1);
5904           const PetscInt oet  = oer + (k - 1);
5905           const PetscInt oel  = oet + (k - 1);
5906           const PetscInt ovlb = oel + (k - 1);
5907           const PetscInt ovrb = ovlb + 1;
5908           const PetscInt ovrt = ovrb + 1;
5909           const PetscInt ovlt = ovrt + 1;
5910           PetscInt       o;
5911 
5912           /* bottom */
5913           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
5914           for (o = oeb; o < oer; ++o)
5915             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5916           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
5917           /* middle */
5918           for (i = 0; i < k - 1; ++i) {
5919             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
5920             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
5921               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5922             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
5923           }
5924           /* top */
5925           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
5926           for (o = oel - 1; o >= oet; --o)
5927             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5928           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
5929           foffset = offset;
5930         } else {
5931           PetscInt dof;
5932 
5933           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
5934           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
5935           foffset = offset;
5936         }
5937         break;
5938       case 3:
5939         /* The original hex closure is
5940 
5941          {c,
5942          f_b, f_t, f_f, f_b, f_r, f_l,
5943          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
5944          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
5945          */
5946         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5947         if (!continuous && d < dim) continue;
5948         /* The SEM order is
5949          Bottom Slice
5950          v_blf, {e^{(k-1)-n}_bf}, v_brf,
5951          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
5952          v_blb, {e_bb}, v_brb,
5953 
5954          Middle Slice (j)
5955          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
5956          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
5957          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
5958 
5959          Top Slice
5960          v_tlf, {e_tf}, v_trf,
5961          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
5962          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
5963          */
5964         if (continuous) {
5965           const PetscInt oc    = 0;
5966           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
5967           const PetscInt oft   = ofb + PetscSqr(k - 1);
5968           const PetscInt off   = oft + PetscSqr(k - 1);
5969           const PetscInt ofk   = off + PetscSqr(k - 1);
5970           const PetscInt ofr   = ofk + PetscSqr(k - 1);
5971           const PetscInt ofl   = ofr + PetscSqr(k - 1);
5972           const PetscInt oebl  = ofl + PetscSqr(k - 1);
5973           const PetscInt oebb  = oebl + (k - 1);
5974           const PetscInt oebr  = oebb + (k - 1);
5975           const PetscInt oebf  = oebr + (k - 1);
5976           const PetscInt oetf  = oebf + (k - 1);
5977           const PetscInt oetr  = oetf + (k - 1);
5978           const PetscInt oetb  = oetr + (k - 1);
5979           const PetscInt oetl  = oetb + (k - 1);
5980           const PetscInt oerf  = oetl + (k - 1);
5981           const PetscInt oelf  = oerf + (k - 1);
5982           const PetscInt oelb  = oelf + (k - 1);
5983           const PetscInt oerb  = oelb + (k - 1);
5984           const PetscInt ovblf = oerb + (k - 1);
5985           const PetscInt ovblb = ovblf + 1;
5986           const PetscInt ovbrb = ovblb + 1;
5987           const PetscInt ovbrf = ovbrb + 1;
5988           const PetscInt ovtlf = ovbrf + 1;
5989           const PetscInt ovtrf = ovtlf + 1;
5990           const PetscInt ovtrb = ovtrf + 1;
5991           const PetscInt ovtlb = ovtrb + 1;
5992           PetscInt       o, n;
5993 
5994           /* Bottom Slice */
5995           /*   bottom */
5996           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
5997           for (o = oetf - 1; o >= oebf; --o)
5998             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5999           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
6000           /*   middle */
6001           for (i = 0; i < k - 1; ++i) {
6002             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
6003             for (n = 0; n < k - 1; ++n) {
6004               o = ofb + n * (k - 1) + i;
6005               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6006             }
6007             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
6008           }
6009           /*   top */
6010           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
6011           for (o = oebb; o < oebr; ++o)
6012             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6013           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
6014 
6015           /* Middle Slice */
6016           for (j = 0; j < k - 1; ++j) {
6017             /*   bottom */
6018             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
6019             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
6020               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6021             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
6022             /*   middle */
6023             for (i = 0; i < k - 1; ++i) {
6024               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
6025               for (n = 0; n < k - 1; ++n)
6026                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
6027               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
6028             }
6029             /*   top */
6030             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
6031             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
6032               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6033             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
6034           }
6035 
6036           /* Top Slice */
6037           /*   bottom */
6038           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
6039           for (o = oetf; o < oetr; ++o)
6040             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6041           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
6042           /*   middle */
6043           for (i = 0; i < k - 1; ++i) {
6044             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
6045             for (n = 0; n < k - 1; ++n)
6046               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
6047             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
6048           }
6049           /*   top */
6050           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
6051           for (o = oetl - 1; o >= oetb; --o)
6052             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6053           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
6054 
6055           foffset = offset;
6056         } else {
6057           PetscInt dof;
6058 
6059           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6060           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6061           foffset = offset;
6062         }
6063         break;
6064       default:
6065         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
6066       }
6067     }
6068     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
6069     /* Check permutation */
6070     {
6071       PetscInt *check;
6072 
6073       PetscCall(PetscMalloc1(size, &check));
6074       for (i = 0; i < size; ++i) {
6075         check[i] = -1;
6076         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
6077       }
6078       for (i = 0; i < size; ++i) check[perm[i]] = i;
6079       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
6080       PetscCall(PetscFree(check));
6081     }
6082     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
6083     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
6084       PetscInt *loc_perm;
6085       PetscCall(PetscMalloc1(size * 2, &loc_perm));
6086       for (PetscInt i = 0; i < size; i++) {
6087         loc_perm[i]        = perm[i];
6088         loc_perm[size + i] = size + perm[i];
6089       }
6090       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
6091     }
6092   }
6093   PetscFunctionReturn(PETSC_SUCCESS);
6094 }
6095 
6096 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
6097 {
6098   PetscDS  prob;
6099   PetscInt depth, Nf, h;
6100   DMLabel  label;
6101 
6102   PetscFunctionBeginHot;
6103   PetscCall(DMGetDS(dm, &prob));
6104   Nf      = prob->Nf;
6105   label   = dm->depthLabel;
6106   *dspace = NULL;
6107   if (field < Nf) {
6108     PetscObject disc = prob->disc[field];
6109 
6110     if (disc->classid == PETSCFE_CLASSID) {
6111       PetscDualSpace dsp;
6112 
6113       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
6114       PetscCall(DMLabelGetNumValues(label, &depth));
6115       PetscCall(DMLabelGetValue(label, point, &h));
6116       h = depth - 1 - h;
6117       if (h) {
6118         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
6119       } else {
6120         *dspace = dsp;
6121       }
6122     }
6123   }
6124   PetscFunctionReturn(PETSC_SUCCESS);
6125 }
6126 
6127 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6128 {
6129   PetscScalar       *array;
6130   const PetscScalar *vArray;
6131   const PetscInt    *cone, *coneO;
6132   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
6133 
6134   PetscFunctionBeginHot;
6135   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6136   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6137   PetscCall(DMPlexGetCone(dm, point, &cone));
6138   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6139   if (!values || !*values) {
6140     if ((point >= pStart) && (point < pEnd)) {
6141       PetscInt dof;
6142 
6143       PetscCall(PetscSectionGetDof(section, point, &dof));
6144       size += dof;
6145     }
6146     for (p = 0; p < numPoints; ++p) {
6147       const PetscInt cp = cone[p];
6148       PetscInt       dof;
6149 
6150       if ((cp < pStart) || (cp >= pEnd)) continue;
6151       PetscCall(PetscSectionGetDof(section, cp, &dof));
6152       size += dof;
6153     }
6154     if (!values) {
6155       if (csize) *csize = size;
6156       PetscFunctionReturn(PETSC_SUCCESS);
6157     }
6158     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
6159   } else {
6160     array = *values;
6161   }
6162   size = 0;
6163   PetscCall(VecGetArrayRead(v, &vArray));
6164   if ((point >= pStart) && (point < pEnd)) {
6165     PetscInt           dof, off, d;
6166     const PetscScalar *varr;
6167 
6168     PetscCall(PetscSectionGetDof(section, point, &dof));
6169     PetscCall(PetscSectionGetOffset(section, point, &off));
6170     varr = PetscSafePointerPlusOffset(vArray, off);
6171     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6172     size += dof;
6173   }
6174   for (p = 0; p < numPoints; ++p) {
6175     const PetscInt     cp = cone[p];
6176     PetscInt           o  = coneO[p];
6177     PetscInt           dof, off, d;
6178     const PetscScalar *varr;
6179 
6180     if ((cp < pStart) || (cp >= pEnd)) continue;
6181     PetscCall(PetscSectionGetDof(section, cp, &dof));
6182     PetscCall(PetscSectionGetOffset(section, cp, &off));
6183     varr = PetscSafePointerPlusOffset(vArray, off);
6184     if (o >= 0) {
6185       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6186     } else {
6187       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
6188     }
6189     size += dof;
6190   }
6191   PetscCall(VecRestoreArrayRead(v, &vArray));
6192   if (!*values) {
6193     if (csize) *csize = size;
6194     *values = array;
6195   } else {
6196     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6197     *csize = size;
6198   }
6199   PetscFunctionReturn(PETSC_SUCCESS);
6200 }
6201 
6202 /* Compress out points not in the section */
6203 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
6204 {
6205   const PetscInt np = *numPoints;
6206   PetscInt       pStart, pEnd, p, q;
6207 
6208   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6209   for (p = 0, q = 0; p < np; ++p) {
6210     const PetscInt r = points[p * 2];
6211     if ((r >= pStart) && (r < pEnd)) {
6212       points[q * 2]     = r;
6213       points[q * 2 + 1] = points[p * 2 + 1];
6214       ++q;
6215     }
6216   }
6217   *numPoints = q;
6218   return PETSC_SUCCESS;
6219 }
6220 
6221 /* Compressed closure does not apply closure permutation */
6222 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt ornt, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6223 {
6224   const PetscInt *cla = NULL;
6225   PetscInt        np, *pts = NULL;
6226 
6227   PetscFunctionBeginHot;
6228   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
6229   if (!ornt && *clPoints) {
6230     PetscInt dof, off;
6231 
6232     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
6233     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
6234     PetscCall(ISGetIndices(*clPoints, &cla));
6235     np  = dof / 2;
6236     pts = PetscSafePointerPlusOffset((PetscInt *)cla, off);
6237   } else {
6238     PetscCall(DMPlexGetTransitiveClosure_Internal(dm, point, ornt, PETSC_TRUE, &np, &pts));
6239     PetscCall(CompressPoints_Private(section, &np, pts));
6240   }
6241   *numPoints = np;
6242   *points    = pts;
6243   *clp       = cla;
6244   PetscFunctionReturn(PETSC_SUCCESS);
6245 }
6246 
6247 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6248 {
6249   PetscFunctionBeginHot;
6250   if (!*clPoints) {
6251     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
6252   } else {
6253     PetscCall(ISRestoreIndices(*clPoints, clp));
6254   }
6255   *numPoints = 0;
6256   *points    = NULL;
6257   *clSec     = NULL;
6258   *clPoints  = NULL;
6259   *clp       = NULL;
6260   PetscFunctionReturn(PETSC_SUCCESS);
6261 }
6262 
6263 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
6264 {
6265   PetscInt            offset = 0, p;
6266   const PetscInt    **perms  = NULL;
6267   const PetscScalar **flips  = NULL;
6268 
6269   PetscFunctionBeginHot;
6270   *size = 0;
6271   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6272   for (p = 0; p < numPoints; p++) {
6273     const PetscInt     point = points[2 * p];
6274     const PetscInt    *perm  = perms ? perms[p] : NULL;
6275     const PetscScalar *flip  = flips ? flips[p] : NULL;
6276     PetscInt           dof, off, d;
6277     const PetscScalar *varr;
6278 
6279     PetscCall(PetscSectionGetDof(section, point, &dof));
6280     PetscCall(PetscSectionGetOffset(section, point, &off));
6281     varr = PetscSafePointerPlusOffset(vArray, off);
6282     if (clperm) {
6283       if (perm) {
6284         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
6285       } else {
6286         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
6287       }
6288       if (flip) {
6289         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
6290       }
6291     } else {
6292       if (perm) {
6293         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
6294       } else {
6295         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
6296       }
6297       if (flip) {
6298         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
6299       }
6300     }
6301     offset += dof;
6302   }
6303   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6304   *size = offset;
6305   PetscFunctionReturn(PETSC_SUCCESS);
6306 }
6307 
6308 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[])
6309 {
6310   PetscInt offset = 0, f;
6311 
6312   PetscFunctionBeginHot;
6313   *size = 0;
6314   for (f = 0; f < numFields; ++f) {
6315     PetscInt            p;
6316     const PetscInt    **perms = NULL;
6317     const PetscScalar **flips = NULL;
6318 
6319     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6320     for (p = 0; p < numPoints; p++) {
6321       const PetscInt     point = points[2 * p];
6322       PetscInt           fdof, foff, b;
6323       const PetscScalar *varr;
6324       const PetscInt    *perm = perms ? perms[p] : NULL;
6325       const PetscScalar *flip = flips ? flips[p] : NULL;
6326 
6327       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6328       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6329       varr = &vArray[foff];
6330       if (clperm) {
6331         if (perm) {
6332           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
6333         } else {
6334           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
6335         }
6336         if (flip) {
6337           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
6338         }
6339       } else {
6340         if (perm) {
6341           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
6342         } else {
6343           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
6344         }
6345         if (flip) {
6346           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
6347         }
6348       }
6349       offset += fdof;
6350     }
6351     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6352   }
6353   *size = offset;
6354   PetscFunctionReturn(PETSC_SUCCESS);
6355 }
6356 
6357 PetscErrorCode DMPlexVecGetOrientedClosure_Internal(DM dm, PetscSection section, PetscBool useClPerm, Vec v, PetscInt point, PetscInt ornt, PetscInt *csize, PetscScalar *values[])
6358 {
6359   PetscSection    clSection;
6360   IS              clPoints;
6361   PetscInt       *points = NULL;
6362   const PetscInt *clp, *perm = NULL;
6363   PetscInt        depth, numFields, numPoints, asize;
6364 
6365   PetscFunctionBeginHot;
6366   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6367   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6368   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6369   PetscValidHeaderSpecific(v, VEC_CLASSID, 4);
6370   PetscCall(DMPlexGetDepth(dm, &depth));
6371   PetscCall(PetscSectionGetNumFields(section, &numFields));
6372   if (depth == 1 && numFields < 2) {
6373     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6374     PetscFunctionReturn(PETSC_SUCCESS);
6375   }
6376   /* Get points */
6377   PetscCall(DMPlexGetCompressedClosure(dm, section, point, ornt, &numPoints, &points, &clSection, &clPoints, &clp));
6378   /* Get sizes */
6379   asize = 0;
6380   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6381     PetscInt dof;
6382     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6383     asize += dof;
6384   }
6385   if (values) {
6386     const PetscScalar *vArray;
6387     PetscInt           size;
6388 
6389     if (*values) {
6390       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);
6391     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6392     if (useClPerm) PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6393     PetscCall(VecGetArrayRead(v, &vArray));
6394     /* Get values */
6395     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6396     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6397     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6398     /* Cleanup array */
6399     PetscCall(VecRestoreArrayRead(v, &vArray));
6400   }
6401   if (csize) *csize = asize;
6402   /* Cleanup points */
6403   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6404   PetscFunctionReturn(PETSC_SUCCESS);
6405 }
6406 
6407 /*@C
6408   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
6409 
6410   Not collective
6411 
6412   Input Parameters:
6413 + dm      - The `DM`
6414 . section - The section describing the layout in `v`, or `NULL` to use the default section
6415 . v       - The local vector
6416 - point   - The point in the `DM`
6417 
6418   Input/Output Parameters:
6419 + csize  - The size of the input values array, or `NULL`; on output the number of values in the closure
6420 - values - An array to use for the values, or `NULL` to have it allocated automatically;
6421            if the user provided `NULL`, it is a borrowed array and should not be freed
6422 
6423   Level: intermediate
6424 
6425   Notes:
6426   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6427   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
6428   assembly function, and a user may already have allocated storage for this operation.
6429 
6430   A typical use could be
6431 .vb
6432    values = NULL;
6433    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6434    for (cl = 0; cl < clSize; ++cl) {
6435      <Compute on closure>
6436    }
6437    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
6438 .ve
6439   or
6440 .vb
6441    PetscMalloc1(clMaxSize, &values);
6442    for (p = pStart; p < pEnd; ++p) {
6443      clSize = clMaxSize;
6444      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6445      for (cl = 0; cl < clSize; ++cl) {
6446        <Compute on closure>
6447      }
6448    }
6449    PetscFree(values);
6450 .ve
6451 
6452   Fortran Notes:
6453   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6454 
6455 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6456 @*/
6457 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6458 {
6459   PetscFunctionBeginHot;
6460   PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, section, PETSC_TRUE, v, point, 0, csize, values));
6461   PetscFunctionReturn(PETSC_SUCCESS);
6462 }
6463 
6464 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6465 {
6466   DMLabel            depthLabel;
6467   PetscSection       clSection;
6468   IS                 clPoints;
6469   PetscScalar       *array;
6470   const PetscScalar *vArray;
6471   PetscInt          *points = NULL;
6472   const PetscInt    *clp, *perm = NULL;
6473   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6474 
6475   PetscFunctionBeginHot;
6476   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6477   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6478   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6479   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6480   PetscCall(DMPlexGetDepth(dm, &mdepth));
6481   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6482   PetscCall(PetscSectionGetNumFields(section, &numFields));
6483   if (mdepth == 1 && numFields < 2) {
6484     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6485     PetscFunctionReturn(PETSC_SUCCESS);
6486   }
6487   /* Get points */
6488   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6489   for (clsize = 0, p = 0; p < Np; p++) {
6490     PetscInt dof;
6491     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6492     clsize += dof;
6493   }
6494   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6495   /* Filter points */
6496   for (p = 0; p < numPoints * 2; p += 2) {
6497     PetscInt dep;
6498 
6499     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6500     if (dep != depth) continue;
6501     points[Np * 2 + 0] = points[p];
6502     points[Np * 2 + 1] = points[p + 1];
6503     ++Np;
6504   }
6505   /* Get array */
6506   if (!values || !*values) {
6507     PetscInt asize = 0, dof;
6508 
6509     for (p = 0; p < Np * 2; p += 2) {
6510       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6511       asize += dof;
6512     }
6513     if (!values) {
6514       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6515       if (csize) *csize = asize;
6516       PetscFunctionReturn(PETSC_SUCCESS);
6517     }
6518     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6519   } else {
6520     array = *values;
6521   }
6522   PetscCall(VecGetArrayRead(v, &vArray));
6523   /* Get values */
6524   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6525   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6526   /* Cleanup points */
6527   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6528   /* Cleanup array */
6529   PetscCall(VecRestoreArrayRead(v, &vArray));
6530   if (!*values) {
6531     if (csize) *csize = size;
6532     *values = array;
6533   } else {
6534     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6535     *csize = size;
6536   }
6537   PetscFunctionReturn(PETSC_SUCCESS);
6538 }
6539 
6540 /*@C
6541   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point'
6542 
6543   Not collective
6544 
6545   Input Parameters:
6546 + dm      - The `DM`
6547 . section - The section describing the layout in `v`, or `NULL` to use the default section
6548 . v       - The local vector
6549 . point   - The point in the `DM`
6550 . csize   - The number of values in the closure, or `NULL`
6551 - values  - The array of values, which is a borrowed array and should not be freed
6552 
6553   Level: intermediate
6554 
6555   Note:
6556   The array values are discarded and not copied back into `v`. In order to copy values back to `v`, use `DMPlexVecSetClosure()`
6557 
6558   Fortran Notes:
6559   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6560 
6561 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6562 @*/
6563 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6564 {
6565   PetscInt size = 0;
6566 
6567   PetscFunctionBegin;
6568   /* Should work without recalculating size */
6569   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6570   *values = NULL;
6571   PetscFunctionReturn(PETSC_SUCCESS);
6572 }
6573 
6574 static inline void add(PetscScalar *x, PetscScalar y)
6575 {
6576   *x += y;
6577 }
6578 static inline void insert(PetscScalar *x, PetscScalar y)
6579 {
6580   *x = y;
6581 }
6582 
6583 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[])
6584 {
6585   PetscInt        cdof;  /* The number of constraints on this point */
6586   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6587   PetscScalar    *a;
6588   PetscInt        off, cind = 0, k;
6589 
6590   PetscFunctionBegin;
6591   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6592   PetscCall(PetscSectionGetOffset(section, point, &off));
6593   a = &array[off];
6594   if (!cdof || setBC) {
6595     if (clperm) {
6596       if (perm) {
6597         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6598       } else {
6599         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6600       }
6601     } else {
6602       if (perm) {
6603         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6604       } else {
6605         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6606       }
6607     }
6608   } else {
6609     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6610     if (clperm) {
6611       if (perm) {
6612         for (k = 0; k < dof; ++k) {
6613           if ((cind < cdof) && (k == cdofs[cind])) {
6614             ++cind;
6615             continue;
6616           }
6617           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6618         }
6619       } else {
6620         for (k = 0; k < dof; ++k) {
6621           if ((cind < cdof) && (k == cdofs[cind])) {
6622             ++cind;
6623             continue;
6624           }
6625           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6626         }
6627       }
6628     } else {
6629       if (perm) {
6630         for (k = 0; k < dof; ++k) {
6631           if ((cind < cdof) && (k == cdofs[cind])) {
6632             ++cind;
6633             continue;
6634           }
6635           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6636         }
6637       } else {
6638         for (k = 0; k < dof; ++k) {
6639           if ((cind < cdof) && (k == cdofs[cind])) {
6640             ++cind;
6641             continue;
6642           }
6643           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6644         }
6645       }
6646     }
6647   }
6648   PetscFunctionReturn(PETSC_SUCCESS);
6649 }
6650 
6651 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[])
6652 {
6653   PetscInt        cdof;  /* The number of constraints on this point */
6654   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6655   PetscScalar    *a;
6656   PetscInt        off, cind = 0, k;
6657 
6658   PetscFunctionBegin;
6659   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6660   PetscCall(PetscSectionGetOffset(section, point, &off));
6661   a = &array[off];
6662   if (cdof) {
6663     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6664     if (clperm) {
6665       if (perm) {
6666         for (k = 0; k < dof; ++k) {
6667           if ((cind < cdof) && (k == cdofs[cind])) {
6668             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6669             cind++;
6670           }
6671         }
6672       } else {
6673         for (k = 0; k < dof; ++k) {
6674           if ((cind < cdof) && (k == cdofs[cind])) {
6675             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6676             cind++;
6677           }
6678         }
6679       }
6680     } else {
6681       if (perm) {
6682         for (k = 0; k < dof; ++k) {
6683           if ((cind < cdof) && (k == cdofs[cind])) {
6684             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6685             cind++;
6686           }
6687         }
6688       } else {
6689         for (k = 0; k < dof; ++k) {
6690           if ((cind < cdof) && (k == cdofs[cind])) {
6691             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6692             cind++;
6693           }
6694         }
6695       }
6696     }
6697   }
6698   PetscFunctionReturn(PETSC_SUCCESS);
6699 }
6700 
6701 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[])
6702 {
6703   PetscScalar    *a;
6704   PetscInt        fdof, foff, fcdof, foffset = *offset;
6705   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6706   PetscInt        cind = 0, b;
6707 
6708   PetscFunctionBegin;
6709   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6710   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6711   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6712   a = &array[foff];
6713   if (!fcdof || setBC) {
6714     if (clperm) {
6715       if (perm) {
6716         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6717       } else {
6718         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6719       }
6720     } else {
6721       if (perm) {
6722         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6723       } else {
6724         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6725       }
6726     }
6727   } else {
6728     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6729     if (clperm) {
6730       if (perm) {
6731         for (b = 0; b < fdof; b++) {
6732           if ((cind < fcdof) && (b == fcdofs[cind])) {
6733             ++cind;
6734             continue;
6735           }
6736           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6737         }
6738       } else {
6739         for (b = 0; b < fdof; b++) {
6740           if ((cind < fcdof) && (b == fcdofs[cind])) {
6741             ++cind;
6742             continue;
6743           }
6744           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6745         }
6746       }
6747     } else {
6748       if (perm) {
6749         for (b = 0; b < fdof; b++) {
6750           if ((cind < fcdof) && (b == fcdofs[cind])) {
6751             ++cind;
6752             continue;
6753           }
6754           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6755         }
6756       } else {
6757         for (b = 0; b < fdof; b++) {
6758           if ((cind < fcdof) && (b == fcdofs[cind])) {
6759             ++cind;
6760             continue;
6761           }
6762           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6763         }
6764       }
6765     }
6766   }
6767   *offset += fdof;
6768   PetscFunctionReturn(PETSC_SUCCESS);
6769 }
6770 
6771 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[])
6772 {
6773   PetscScalar    *a;
6774   PetscInt        fdof, foff, fcdof, foffset = *offset;
6775   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6776   PetscInt        Nc, cind = 0, ncind = 0, b;
6777   PetscBool       ncSet, fcSet;
6778 
6779   PetscFunctionBegin;
6780   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6781   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6782   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6783   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6784   a = &array[foff];
6785   if (fcdof) {
6786     /* We just override fcdof and fcdofs with Ncc and comps */
6787     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6788     if (clperm) {
6789       if (perm) {
6790         if (comps) {
6791           for (b = 0; b < fdof; b++) {
6792             ncSet = fcSet = PETSC_FALSE;
6793             if (b % Nc == comps[ncind]) {
6794               ncind = (ncind + 1) % Ncc;
6795               ncSet = PETSC_TRUE;
6796             }
6797             if ((cind < fcdof) && (b == fcdofs[cind])) {
6798               ++cind;
6799               fcSet = PETSC_TRUE;
6800             }
6801             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6802           }
6803         } else {
6804           for (b = 0; b < fdof; b++) {
6805             if ((cind < fcdof) && (b == fcdofs[cind])) {
6806               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6807               ++cind;
6808             }
6809           }
6810         }
6811       } else {
6812         if (comps) {
6813           for (b = 0; b < fdof; b++) {
6814             ncSet = fcSet = PETSC_FALSE;
6815             if (b % Nc == comps[ncind]) {
6816               ncind = (ncind + 1) % Ncc;
6817               ncSet = PETSC_TRUE;
6818             }
6819             if ((cind < fcdof) && (b == fcdofs[cind])) {
6820               ++cind;
6821               fcSet = PETSC_TRUE;
6822             }
6823             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6824           }
6825         } else {
6826           for (b = 0; b < fdof; b++) {
6827             if ((cind < fcdof) && (b == fcdofs[cind])) {
6828               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6829               ++cind;
6830             }
6831           }
6832         }
6833       }
6834     } else {
6835       if (perm) {
6836         if (comps) {
6837           for (b = 0; b < fdof; b++) {
6838             ncSet = fcSet = PETSC_FALSE;
6839             if (b % Nc == comps[ncind]) {
6840               ncind = (ncind + 1) % Ncc;
6841               ncSet = PETSC_TRUE;
6842             }
6843             if ((cind < fcdof) && (b == fcdofs[cind])) {
6844               ++cind;
6845               fcSet = PETSC_TRUE;
6846             }
6847             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6848           }
6849         } else {
6850           for (b = 0; b < fdof; b++) {
6851             if ((cind < fcdof) && (b == fcdofs[cind])) {
6852               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6853               ++cind;
6854             }
6855           }
6856         }
6857       } else {
6858         if (comps) {
6859           for (b = 0; b < fdof; b++) {
6860             ncSet = fcSet = PETSC_FALSE;
6861             if (b % Nc == comps[ncind]) {
6862               ncind = (ncind + 1) % Ncc;
6863               ncSet = PETSC_TRUE;
6864             }
6865             if ((cind < fcdof) && (b == fcdofs[cind])) {
6866               ++cind;
6867               fcSet = PETSC_TRUE;
6868             }
6869             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6870           }
6871         } else {
6872           for (b = 0; b < fdof; b++) {
6873             if ((cind < fcdof) && (b == fcdofs[cind])) {
6874               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6875               ++cind;
6876             }
6877           }
6878         }
6879       }
6880     }
6881   }
6882   *offset += fdof;
6883   PetscFunctionReturn(PETSC_SUCCESS);
6884 }
6885 
6886 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6887 {
6888   PetscScalar    *array;
6889   const PetscInt *cone, *coneO;
6890   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6891 
6892   PetscFunctionBeginHot;
6893   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6894   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6895   PetscCall(DMPlexGetCone(dm, point, &cone));
6896   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6897   PetscCall(VecGetArray(v, &array));
6898   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6899     const PetscInt cp = !p ? point : cone[p - 1];
6900     const PetscInt o  = !p ? 0 : coneO[p - 1];
6901 
6902     if ((cp < pStart) || (cp >= pEnd)) {
6903       dof = 0;
6904       continue;
6905     }
6906     PetscCall(PetscSectionGetDof(section, cp, &dof));
6907     /* ADD_VALUES */
6908     {
6909       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6910       PetscScalar    *a;
6911       PetscInt        cdof, coff, cind = 0, k;
6912 
6913       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
6914       PetscCall(PetscSectionGetOffset(section, cp, &coff));
6915       a = &array[coff];
6916       if (!cdof) {
6917         if (o >= 0) {
6918           for (k = 0; k < dof; ++k) a[k] += values[off + k];
6919         } else {
6920           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
6921         }
6922       } else {
6923         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
6924         if (o >= 0) {
6925           for (k = 0; k < dof; ++k) {
6926             if ((cind < cdof) && (k == cdofs[cind])) {
6927               ++cind;
6928               continue;
6929             }
6930             a[k] += values[off + k];
6931           }
6932         } else {
6933           for (k = 0; k < dof; ++k) {
6934             if ((cind < cdof) && (k == cdofs[cind])) {
6935               ++cind;
6936               continue;
6937             }
6938             a[k] += values[off + dof - k - 1];
6939           }
6940         }
6941       }
6942     }
6943   }
6944   PetscCall(VecRestoreArray(v, &array));
6945   PetscFunctionReturn(PETSC_SUCCESS);
6946 }
6947 
6948 /*@C
6949   DMPlexVecSetClosure - Set an array of the values on the closure of `point`
6950 
6951   Not collective
6952 
6953   Input Parameters:
6954 + dm      - The `DM`
6955 . section - The section describing the layout in `v`, or `NULL` to use the default section
6956 . v       - The local vector
6957 . point   - The point in the `DM`
6958 . values  - The array of values
6959 - mode    - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
6960          where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
6961 
6962   Level: intermediate
6963 
6964 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
6965 @*/
6966 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6967 {
6968   PetscSection    clSection;
6969   IS              clPoints;
6970   PetscScalar    *array;
6971   PetscInt       *points = NULL;
6972   const PetscInt *clp, *clperm = NULL;
6973   PetscInt        depth, numFields, numPoints, p, clsize;
6974 
6975   PetscFunctionBeginHot;
6976   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6977   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6978   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6979   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6980   PetscCall(DMPlexGetDepth(dm, &depth));
6981   PetscCall(PetscSectionGetNumFields(section, &numFields));
6982   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
6983     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
6984     PetscFunctionReturn(PETSC_SUCCESS);
6985   }
6986   /* Get points */
6987   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6988   for (clsize = 0, p = 0; p < numPoints; p++) {
6989     PetscInt dof;
6990     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6991     clsize += dof;
6992   }
6993   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
6994   /* Get array */
6995   PetscCall(VecGetArray(v, &array));
6996   /* Get values */
6997   if (numFields > 0) {
6998     PetscInt offset = 0, f;
6999     for (f = 0; f < numFields; ++f) {
7000       const PetscInt    **perms = NULL;
7001       const PetscScalar **flips = NULL;
7002 
7003       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7004       switch (mode) {
7005       case INSERT_VALUES:
7006         for (p = 0; p < numPoints; p++) {
7007           const PetscInt     point = points[2 * p];
7008           const PetscInt    *perm  = perms ? perms[p] : NULL;
7009           const PetscScalar *flip  = flips ? flips[p] : NULL;
7010           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array));
7011         }
7012         break;
7013       case INSERT_ALL_VALUES:
7014         for (p = 0; p < numPoints; p++) {
7015           const PetscInt     point = points[2 * p];
7016           const PetscInt    *perm  = perms ? perms[p] : NULL;
7017           const PetscScalar *flip  = flips ? flips[p] : NULL;
7018           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array));
7019         }
7020         break;
7021       case INSERT_BC_VALUES:
7022         for (p = 0; p < numPoints; p++) {
7023           const PetscInt     point = points[2 * p];
7024           const PetscInt    *perm  = perms ? perms[p] : NULL;
7025           const PetscScalar *flip  = flips ? flips[p] : NULL;
7026           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array));
7027         }
7028         break;
7029       case ADD_VALUES:
7030         for (p = 0; p < numPoints; p++) {
7031           const PetscInt     point = points[2 * p];
7032           const PetscInt    *perm  = perms ? perms[p] : NULL;
7033           const PetscScalar *flip  = flips ? flips[p] : NULL;
7034           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array));
7035         }
7036         break;
7037       case ADD_ALL_VALUES:
7038         for (p = 0; p < numPoints; p++) {
7039           const PetscInt     point = points[2 * p];
7040           const PetscInt    *perm  = perms ? perms[p] : NULL;
7041           const PetscScalar *flip  = flips ? flips[p] : NULL;
7042           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array));
7043         }
7044         break;
7045       case ADD_BC_VALUES:
7046         for (p = 0; p < numPoints; p++) {
7047           const PetscInt     point = points[2 * p];
7048           const PetscInt    *perm  = perms ? perms[p] : NULL;
7049           const PetscScalar *flip  = flips ? flips[p] : NULL;
7050           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array));
7051         }
7052         break;
7053       default:
7054         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7055       }
7056       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7057     }
7058   } else {
7059     PetscInt            dof, off;
7060     const PetscInt    **perms = NULL;
7061     const PetscScalar **flips = NULL;
7062 
7063     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
7064     switch (mode) {
7065     case INSERT_VALUES:
7066       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7067         const PetscInt     point = points[2 * p];
7068         const PetscInt    *perm  = perms ? perms[p] : NULL;
7069         const PetscScalar *flip  = flips ? flips[p] : NULL;
7070         PetscCall(PetscSectionGetDof(section, point, &dof));
7071         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array));
7072       }
7073       break;
7074     case INSERT_ALL_VALUES:
7075       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7076         const PetscInt     point = points[2 * p];
7077         const PetscInt    *perm  = perms ? perms[p] : NULL;
7078         const PetscScalar *flip  = flips ? flips[p] : NULL;
7079         PetscCall(PetscSectionGetDof(section, point, &dof));
7080         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array));
7081       }
7082       break;
7083     case INSERT_BC_VALUES:
7084       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7085         const PetscInt     point = points[2 * p];
7086         const PetscInt    *perm  = perms ? perms[p] : NULL;
7087         const PetscScalar *flip  = flips ? flips[p] : NULL;
7088         PetscCall(PetscSectionGetDof(section, point, &dof));
7089         PetscCall(updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array));
7090       }
7091       break;
7092     case ADD_VALUES:
7093       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7094         const PetscInt     point = points[2 * p];
7095         const PetscInt    *perm  = perms ? perms[p] : NULL;
7096         const PetscScalar *flip  = flips ? flips[p] : NULL;
7097         PetscCall(PetscSectionGetDof(section, point, &dof));
7098         PetscCall(updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array));
7099       }
7100       break;
7101     case ADD_ALL_VALUES:
7102       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7103         const PetscInt     point = points[2 * p];
7104         const PetscInt    *perm  = perms ? perms[p] : NULL;
7105         const PetscScalar *flip  = flips ? flips[p] : NULL;
7106         PetscCall(PetscSectionGetDof(section, point, &dof));
7107         PetscCall(updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array));
7108       }
7109       break;
7110     case ADD_BC_VALUES:
7111       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7112         const PetscInt     point = points[2 * p];
7113         const PetscInt    *perm  = perms ? perms[p] : NULL;
7114         const PetscScalar *flip  = flips ? flips[p] : NULL;
7115         PetscCall(PetscSectionGetDof(section, point, &dof));
7116         PetscCall(updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array));
7117       }
7118       break;
7119     default:
7120       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7121     }
7122     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
7123   }
7124   /* Cleanup points */
7125   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7126   /* Cleanup array */
7127   PetscCall(VecRestoreArray(v, &array));
7128   PetscFunctionReturn(PETSC_SUCCESS);
7129 }
7130 
7131 /* Check whether the given point is in the label. If not, update the offset to skip this point */
7132 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
7133 {
7134   PetscFunctionBegin;
7135   *contains = PETSC_TRUE;
7136   if (label) {
7137     PetscInt fdof;
7138 
7139     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
7140     if (!*contains) {
7141       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7142       *offset += fdof;
7143       PetscFunctionReturn(PETSC_SUCCESS);
7144     }
7145   }
7146   PetscFunctionReturn(PETSC_SUCCESS);
7147 }
7148 
7149 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
7150 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)
7151 {
7152   PetscSection    clSection;
7153   IS              clPoints;
7154   PetscScalar    *array;
7155   PetscInt       *points = NULL;
7156   const PetscInt *clp;
7157   PetscInt        numFields, numPoints, p;
7158   PetscInt        offset = 0, f;
7159 
7160   PetscFunctionBeginHot;
7161   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7162   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7163   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7164   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7165   PetscCall(PetscSectionGetNumFields(section, &numFields));
7166   /* Get points */
7167   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7168   /* Get array */
7169   PetscCall(VecGetArray(v, &array));
7170   /* Get values */
7171   for (f = 0; f < numFields; ++f) {
7172     const PetscInt    **perms = NULL;
7173     const PetscScalar **flips = NULL;
7174     PetscBool           contains;
7175 
7176     if (!fieldActive[f]) {
7177       for (p = 0; p < numPoints * 2; p += 2) {
7178         PetscInt fdof;
7179         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7180         offset += fdof;
7181       }
7182       continue;
7183     }
7184     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7185     switch (mode) {
7186     case INSERT_VALUES:
7187       for (p = 0; p < numPoints; p++) {
7188         const PetscInt     point = points[2 * p];
7189         const PetscInt    *perm  = perms ? perms[p] : NULL;
7190         const PetscScalar *flip  = flips ? flips[p] : NULL;
7191         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7192         if (!contains) continue;
7193         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
7194       }
7195       break;
7196     case INSERT_ALL_VALUES:
7197       for (p = 0; p < numPoints; p++) {
7198         const PetscInt     point = points[2 * p];
7199         const PetscInt    *perm  = perms ? perms[p] : NULL;
7200         const PetscScalar *flip  = flips ? flips[p] : NULL;
7201         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7202         if (!contains) continue;
7203         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
7204       }
7205       break;
7206     case INSERT_BC_VALUES:
7207       for (p = 0; p < numPoints; p++) {
7208         const PetscInt     point = points[2 * p];
7209         const PetscInt    *perm  = perms ? perms[p] : NULL;
7210         const PetscScalar *flip  = flips ? flips[p] : NULL;
7211         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7212         if (!contains) continue;
7213         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
7214       }
7215       break;
7216     case ADD_VALUES:
7217       for (p = 0; p < numPoints; p++) {
7218         const PetscInt     point = points[2 * p];
7219         const PetscInt    *perm  = perms ? perms[p] : NULL;
7220         const PetscScalar *flip  = flips ? flips[p] : NULL;
7221         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7222         if (!contains) continue;
7223         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
7224       }
7225       break;
7226     case ADD_ALL_VALUES:
7227       for (p = 0; p < numPoints; p++) {
7228         const PetscInt     point = points[2 * p];
7229         const PetscInt    *perm  = perms ? perms[p] : NULL;
7230         const PetscScalar *flip  = flips ? flips[p] : NULL;
7231         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7232         if (!contains) continue;
7233         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
7234       }
7235       break;
7236     default:
7237       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7238     }
7239     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7240   }
7241   /* Cleanup points */
7242   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7243   /* Cleanup array */
7244   PetscCall(VecRestoreArray(v, &array));
7245   PetscFunctionReturn(PETSC_SUCCESS);
7246 }
7247 
7248 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
7249 {
7250   PetscMPIInt rank;
7251   PetscInt    i, j;
7252 
7253   PetscFunctionBegin;
7254   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7255   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
7256   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
7257   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
7258   numCIndices = numCIndices ? numCIndices : numRIndices;
7259   if (!values) PetscFunctionReturn(PETSC_SUCCESS);
7260   for (i = 0; i < numRIndices; i++) {
7261     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
7262     for (j = 0; j < numCIndices; j++) {
7263 #if defined(PETSC_USE_COMPLEX)
7264       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
7265 #else
7266       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
7267 #endif
7268     }
7269     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
7270   }
7271   PetscFunctionReturn(PETSC_SUCCESS);
7272 }
7273 
7274 /*
7275   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
7276 
7277   Input Parameters:
7278 + section - The section for this data layout
7279 . islocal - Is the section (and thus indices being requested) local or global?
7280 . point   - The point contributing dofs with these indices
7281 . off     - The global offset of this point
7282 . loff    - The local offset of each field
7283 . setBC   - The flag determining whether to include indices of boundary values
7284 . perm    - A permutation of the dofs on this point, or NULL
7285 - indperm - A permutation of the entire indices array, or NULL
7286 
7287   Output Parameter:
7288 . indices - Indices for dofs on this point
7289 
7290   Level: developer
7291 
7292   Note: The indices could be local or global, depending on the value of 'off'.
7293 */
7294 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
7295 {
7296   PetscInt        dof;   /* The number of unknowns on this point */
7297   PetscInt        cdof;  /* The number of constraints on this point */
7298   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7299   PetscInt        cind = 0, k;
7300 
7301   PetscFunctionBegin;
7302   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7303   PetscCall(PetscSectionGetDof(section, point, &dof));
7304   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
7305   if (!cdof || setBC) {
7306     for (k = 0; k < dof; ++k) {
7307       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7308       const PetscInt ind    = indperm ? indperm[preind] : preind;
7309 
7310       indices[ind] = off + k;
7311     }
7312   } else {
7313     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
7314     for (k = 0; k < dof; ++k) {
7315       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7316       const PetscInt ind    = indperm ? indperm[preind] : preind;
7317 
7318       if ((cind < cdof) && (k == cdofs[cind])) {
7319         /* Insert check for returning constrained indices */
7320         indices[ind] = -(off + k + 1);
7321         ++cind;
7322       } else {
7323         indices[ind] = off + k - (islocal ? 0 : cind);
7324       }
7325     }
7326   }
7327   *loff += dof;
7328   PetscFunctionReturn(PETSC_SUCCESS);
7329 }
7330 
7331 /*
7332  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
7333 
7334  Input Parameters:
7335 + section - a section (global or local)
7336 - islocal - `PETSC_TRUE` if requesting local indices (i.e., section is local); `PETSC_FALSE` for global
7337 . point - point within section
7338 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
7339 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
7340 . setBC - identify constrained (boundary condition) points via involution.
7341 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
7342 . permsoff - offset
7343 - indperm - index permutation
7344 
7345  Output Parameter:
7346 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
7347 . indices - array to hold indices (as defined by section) of each dof associated with point
7348 
7349  Notes:
7350  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
7351  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
7352  in the local vector.
7353 
7354  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
7355  significant).  It is invalid to call with a global section and setBC=true.
7356 
7357  Developer Note:
7358  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
7359  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
7360  offset could be obtained from the section instead of passing it explicitly as we do now.
7361 
7362  Example:
7363  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
7364  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
7365  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
7366  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.
7367 
7368  Level: developer
7369 */
7370 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[])
7371 {
7372   PetscInt numFields, foff, f;
7373 
7374   PetscFunctionBegin;
7375   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7376   PetscCall(PetscSectionGetNumFields(section, &numFields));
7377   for (f = 0, foff = 0; f < numFields; ++f) {
7378     PetscInt        fdof, cfdof;
7379     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7380     PetscInt        cind = 0, b;
7381     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7382 
7383     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7384     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7385     if (!cfdof || setBC) {
7386       for (b = 0; b < fdof; ++b) {
7387         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7388         const PetscInt ind    = indperm ? indperm[preind] : preind;
7389 
7390         indices[ind] = off + foff + b;
7391       }
7392     } else {
7393       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7394       for (b = 0; b < fdof; ++b) {
7395         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7396         const PetscInt ind    = indperm ? indperm[preind] : preind;
7397 
7398         if ((cind < cfdof) && (b == fcdofs[cind])) {
7399           indices[ind] = -(off + foff + b + 1);
7400           ++cind;
7401         } else {
7402           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7403         }
7404       }
7405     }
7406     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7407     foffs[f] += fdof;
7408   }
7409   PetscFunctionReturn(PETSC_SUCCESS);
7410 }
7411 
7412 /*
7413   This version believes the globalSection offsets for each field, rather than just the point offset
7414 
7415  . foffs - The offset into 'indices' for each field, since it is segregated by field
7416 
7417  Notes:
7418  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7419  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7420 */
7421 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7422 {
7423   PetscInt numFields, foff, f;
7424 
7425   PetscFunctionBegin;
7426   PetscCall(PetscSectionGetNumFields(section, &numFields));
7427   for (f = 0; f < numFields; ++f) {
7428     PetscInt        fdof, cfdof;
7429     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7430     PetscInt        cind = 0, b;
7431     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7432 
7433     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7434     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7435     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7436     if (!cfdof) {
7437       for (b = 0; b < fdof; ++b) {
7438         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7439         const PetscInt ind    = indperm ? indperm[preind] : preind;
7440 
7441         indices[ind] = foff + b;
7442       }
7443     } else {
7444       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7445       for (b = 0; b < fdof; ++b) {
7446         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7447         const PetscInt ind    = indperm ? indperm[preind] : preind;
7448 
7449         if ((cind < cfdof) && (b == fcdofs[cind])) {
7450           indices[ind] = -(foff + b + 1);
7451           ++cind;
7452         } else {
7453           indices[ind] = foff + b - cind;
7454         }
7455       }
7456     }
7457     foffs[f] += fdof;
7458   }
7459   PetscFunctionReturn(PETSC_SUCCESS);
7460 }
7461 
7462 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)
7463 {
7464   Mat             cMat;
7465   PetscSection    aSec, cSec;
7466   IS              aIS;
7467   PetscInt        aStart = -1, aEnd = -1;
7468   const PetscInt *anchors;
7469   PetscInt        numFields, f, p, q, newP = 0;
7470   PetscInt        newNumPoints = 0, newNumIndices = 0;
7471   PetscInt       *newPoints, *indices, *newIndices;
7472   PetscInt        maxAnchor, maxDof;
7473   PetscInt        newOffsets[32];
7474   PetscInt       *pointMatOffsets[32];
7475   PetscInt       *newPointOffsets[32];
7476   PetscScalar    *pointMat[32];
7477   PetscScalar    *newValues      = NULL, *tmpValues;
7478   PetscBool       anyConstrained = PETSC_FALSE;
7479 
7480   PetscFunctionBegin;
7481   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7482   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7483   PetscCall(PetscSectionGetNumFields(section, &numFields));
7484 
7485   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7486   /* if there are point-to-point constraints */
7487   if (aSec) {
7488     PetscCall(PetscArrayzero(newOffsets, 32));
7489     PetscCall(ISGetIndices(aIS, &anchors));
7490     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7491     /* figure out how many points are going to be in the new element matrix
7492      * (we allow double counting, because it's all just going to be summed
7493      * into the global matrix anyway) */
7494     for (p = 0; p < 2 * numPoints; p += 2) {
7495       PetscInt b    = points[p];
7496       PetscInt bDof = 0, bSecDof;
7497 
7498       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7499       if (!bSecDof) continue;
7500       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7501       if (bDof) {
7502         /* this point is constrained */
7503         /* it is going to be replaced by its anchors */
7504         PetscInt bOff, q;
7505 
7506         anyConstrained = PETSC_TRUE;
7507         newNumPoints += bDof;
7508         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7509         for (q = 0; q < bDof; q++) {
7510           PetscInt a = anchors[bOff + q];
7511           PetscInt aDof;
7512 
7513           PetscCall(PetscSectionGetDof(section, a, &aDof));
7514           newNumIndices += aDof;
7515           for (f = 0; f < numFields; ++f) {
7516             PetscInt fDof;
7517 
7518             PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7519             newOffsets[f + 1] += fDof;
7520           }
7521         }
7522       } else {
7523         /* this point is not constrained */
7524         newNumPoints++;
7525         newNumIndices += bSecDof;
7526         for (f = 0; f < numFields; ++f) {
7527           PetscInt fDof;
7528 
7529           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7530           newOffsets[f + 1] += fDof;
7531         }
7532       }
7533     }
7534   }
7535   if (!anyConstrained) {
7536     if (outNumPoints) *outNumPoints = 0;
7537     if (outNumIndices) *outNumIndices = 0;
7538     if (outPoints) *outPoints = NULL;
7539     if (outValues) *outValues = NULL;
7540     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7541     PetscFunctionReturn(PETSC_SUCCESS);
7542   }
7543 
7544   if (outNumPoints) *outNumPoints = newNumPoints;
7545   if (outNumIndices) *outNumIndices = newNumIndices;
7546 
7547   for (f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7548 
7549   if (!outPoints && !outValues) {
7550     if (offsets) {
7551       for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7552     }
7553     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7554     PetscFunctionReturn(PETSC_SUCCESS);
7555   }
7556 
7557   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7558 
7559   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7560 
7561   /* workspaces */
7562   if (numFields) {
7563     for (f = 0; f < numFields; f++) {
7564       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7565       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7566     }
7567   } else {
7568     PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7569     PetscCall(DMGetWorkArray(dm, numPoints, MPIU_INT, &newPointOffsets[0]));
7570   }
7571 
7572   /* get workspaces for the point-to-point matrices */
7573   if (numFields) {
7574     PetscInt totalOffset, totalMatOffset;
7575 
7576     for (p = 0; p < numPoints; p++) {
7577       PetscInt b    = points[2 * p];
7578       PetscInt bDof = 0, bSecDof;
7579 
7580       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7581       if (!bSecDof) {
7582         for (f = 0; f < numFields; f++) {
7583           newPointOffsets[f][p + 1] = 0;
7584           pointMatOffsets[f][p + 1] = 0;
7585         }
7586         continue;
7587       }
7588       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7589       if (bDof) {
7590         for (f = 0; f < numFields; f++) {
7591           PetscInt fDof, q, bOff, allFDof = 0;
7592 
7593           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7594           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7595           for (q = 0; q < bDof; q++) {
7596             PetscInt a = anchors[bOff + q];
7597             PetscInt aFDof;
7598 
7599             PetscCall(PetscSectionGetFieldDof(section, a, f, &aFDof));
7600             allFDof += aFDof;
7601           }
7602           newPointOffsets[f][p + 1] = allFDof;
7603           pointMatOffsets[f][p + 1] = fDof * allFDof;
7604         }
7605       } else {
7606         for (f = 0; f < numFields; f++) {
7607           PetscInt fDof;
7608 
7609           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7610           newPointOffsets[f][p + 1] = fDof;
7611           pointMatOffsets[f][p + 1] = 0;
7612         }
7613       }
7614     }
7615     for (f = 0, totalOffset = 0, totalMatOffset = 0; f < numFields; f++) {
7616       newPointOffsets[f][0] = totalOffset;
7617       pointMatOffsets[f][0] = totalMatOffset;
7618       for (p = 0; p < numPoints; p++) {
7619         newPointOffsets[f][p + 1] += newPointOffsets[f][p];
7620         pointMatOffsets[f][p + 1] += pointMatOffsets[f][p];
7621       }
7622       totalOffset    = newPointOffsets[f][numPoints];
7623       totalMatOffset = pointMatOffsets[f][numPoints];
7624       PetscCall(DMGetWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7625     }
7626   } else {
7627     for (p = 0; p < numPoints; p++) {
7628       PetscInt b    = points[2 * p];
7629       PetscInt bDof = 0, bSecDof;
7630 
7631       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7632       if (!bSecDof) {
7633         newPointOffsets[0][p + 1] = 0;
7634         pointMatOffsets[0][p + 1] = 0;
7635         continue;
7636       }
7637       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7638       if (bDof) {
7639         PetscInt bOff, q, allDof = 0;
7640 
7641         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7642         for (q = 0; q < bDof; q++) {
7643           PetscInt a = anchors[bOff + q], aDof;
7644 
7645           PetscCall(PetscSectionGetDof(section, a, &aDof));
7646           allDof += aDof;
7647         }
7648         newPointOffsets[0][p + 1] = allDof;
7649         pointMatOffsets[0][p + 1] = bSecDof * allDof;
7650       } else {
7651         newPointOffsets[0][p + 1] = bSecDof;
7652         pointMatOffsets[0][p + 1] = 0;
7653       }
7654     }
7655     newPointOffsets[0][0] = 0;
7656     pointMatOffsets[0][0] = 0;
7657     for (p = 0; p < numPoints; p++) {
7658       newPointOffsets[0][p + 1] += newPointOffsets[0][p];
7659       pointMatOffsets[0][p + 1] += pointMatOffsets[0][p];
7660     }
7661     PetscCall(DMGetWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7662   }
7663 
7664   /* output arrays */
7665   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7666 
7667   /* get the point-to-point matrices; construct newPoints */
7668   PetscCall(PetscSectionGetMaxDof(aSec, &maxAnchor));
7669   PetscCall(PetscSectionGetMaxDof(section, &maxDof));
7670   PetscCall(DMGetWorkArray(dm, maxDof, MPIU_INT, &indices));
7671   PetscCall(DMGetWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7672   if (numFields) {
7673     for (p = 0, newP = 0; p < numPoints; p++) {
7674       PetscInt b    = points[2 * p];
7675       PetscInt o    = points[2 * p + 1];
7676       PetscInt bDof = 0, bSecDof;
7677 
7678       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7679       if (!bSecDof) continue;
7680       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7681       if (bDof) {
7682         PetscInt fStart[32], fEnd[32], fAnchorStart[32], fAnchorEnd[32], bOff, q;
7683 
7684         fStart[0] = 0;
7685         fEnd[0]   = 0;
7686         for (f = 0; f < numFields; f++) {
7687           PetscInt fDof;
7688 
7689           PetscCall(PetscSectionGetFieldDof(cSec, b, f, &fDof));
7690           fStart[f + 1] = fStart[f] + fDof;
7691           fEnd[f + 1]   = fStart[f + 1];
7692         }
7693         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7694         PetscCall(DMPlexGetIndicesPointFields_Internal(cSec, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, p, NULL, indices));
7695 
7696         fAnchorStart[0] = 0;
7697         fAnchorEnd[0]   = 0;
7698         for (f = 0; f < numFields; f++) {
7699           PetscInt fDof = newPointOffsets[f][p + 1] - newPointOffsets[f][p];
7700 
7701           fAnchorStart[f + 1] = fAnchorStart[f] + fDof;
7702           fAnchorEnd[f + 1]   = fAnchorStart[f + 1];
7703         }
7704         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7705         for (q = 0; q < bDof; q++) {
7706           PetscInt a = anchors[bOff + q], aOff;
7707 
7708           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7709           newPoints[2 * (newP + q)]     = a;
7710           newPoints[2 * (newP + q) + 1] = 0;
7711           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7712           PetscCall(DMPlexGetIndicesPointFields_Internal(section, PETSC_TRUE, a, aOff, fAnchorEnd, PETSC_TRUE, NULL, -1, NULL, newIndices));
7713         }
7714         newP += bDof;
7715 
7716         if (outValues) {
7717           /* get the point-to-point submatrix */
7718           for (f = 0; f < numFields; f++) PetscCall(MatGetValues(cMat, fEnd[f] - fStart[f], indices + fStart[f], fAnchorEnd[f] - fAnchorStart[f], newIndices + fAnchorStart[f], pointMat[f] + pointMatOffsets[f][p]));
7719         }
7720       } else {
7721         newPoints[2 * newP]     = b;
7722         newPoints[2 * newP + 1] = o;
7723         newP++;
7724       }
7725     }
7726   } else {
7727     for (p = 0; p < numPoints; p++) {
7728       PetscInt b    = points[2 * p];
7729       PetscInt o    = points[2 * p + 1];
7730       PetscInt bDof = 0, bSecDof;
7731 
7732       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7733       if (!bSecDof) continue;
7734       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7735       if (bDof) {
7736         PetscInt bEnd = 0, bAnchorEnd = 0, bOff;
7737 
7738         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7739         PetscCall(DMPlexGetIndicesPoint_Internal(cSec, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, indices));
7740 
7741         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7742         for (q = 0; q < bDof; q++) {
7743           PetscInt a = anchors[bOff + q], aOff;
7744 
7745           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7746 
7747           newPoints[2 * (newP + q)]     = a;
7748           newPoints[2 * (newP + q) + 1] = 0;
7749           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7750           PetscCall(DMPlexGetIndicesPoint_Internal(section, PETSC_TRUE, a, aOff, &bAnchorEnd, PETSC_TRUE, NULL, NULL, newIndices));
7751         }
7752         newP += bDof;
7753 
7754         /* get the point-to-point submatrix */
7755         if (outValues) PetscCall(MatGetValues(cMat, bEnd, indices, bAnchorEnd, newIndices, pointMat[0] + pointMatOffsets[0][p]));
7756       } else {
7757         newPoints[2 * newP]     = b;
7758         newPoints[2 * newP + 1] = o;
7759         newP++;
7760       }
7761     }
7762   }
7763 
7764   if (outValues) {
7765     PetscCall(DMGetWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7766     PetscCall(PetscArrayzero(tmpValues, newNumIndices * numIndices));
7767     /* multiply constraints on the right */
7768     if (numFields) {
7769       for (f = 0; f < numFields; f++) {
7770         PetscInt oldOff = offsets[f];
7771 
7772         for (p = 0; p < numPoints; p++) {
7773           PetscInt cStart = newPointOffsets[f][p];
7774           PetscInt b      = points[2 * p];
7775           PetscInt c, r, k;
7776           PetscInt dof;
7777 
7778           PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7779           if (!dof) continue;
7780           if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7781             PetscInt           nCols = newPointOffsets[f][p + 1] - cStart;
7782             const PetscScalar *mat   = pointMat[f] + pointMatOffsets[f][p];
7783 
7784             for (r = 0; r < numIndices; r++) {
7785               for (c = 0; c < nCols; c++) {
7786                 for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += values[r * numIndices + oldOff + k] * mat[k * nCols + c];
7787               }
7788             }
7789           } else {
7790             /* copy this column as is */
7791             for (r = 0; r < numIndices; r++) {
7792               for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7793             }
7794           }
7795           oldOff += dof;
7796         }
7797       }
7798     } else {
7799       PetscInt oldOff = 0;
7800       for (p = 0; p < numPoints; p++) {
7801         PetscInt cStart = newPointOffsets[0][p];
7802         PetscInt b      = points[2 * p];
7803         PetscInt c, r, k;
7804         PetscInt dof;
7805 
7806         PetscCall(PetscSectionGetDof(section, b, &dof));
7807         if (!dof) continue;
7808         if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7809           PetscInt           nCols = newPointOffsets[0][p + 1] - cStart;
7810           const PetscScalar *mat   = pointMat[0] + pointMatOffsets[0][p];
7811 
7812           for (r = 0; r < numIndices; r++) {
7813             for (c = 0; c < nCols; c++) {
7814               for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += mat[k * nCols + c] * values[r * numIndices + oldOff + k];
7815             }
7816           }
7817         } else {
7818           /* copy this column as is */
7819           for (r = 0; r < numIndices; r++) {
7820             for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7821           }
7822         }
7823         oldOff += dof;
7824       }
7825     }
7826 
7827     if (multiplyLeft) {
7828       PetscCall(DMGetWorkArray(dm, newNumIndices * newNumIndices, MPIU_SCALAR, &newValues));
7829       PetscCall(PetscArrayzero(newValues, newNumIndices * newNumIndices));
7830       /* multiply constraints transpose on the left */
7831       if (numFields) {
7832         for (f = 0; f < numFields; f++) {
7833           PetscInt oldOff = offsets[f];
7834 
7835           for (p = 0; p < numPoints; p++) {
7836             PetscInt rStart = newPointOffsets[f][p];
7837             PetscInt b      = points[2 * p];
7838             PetscInt c, r, k;
7839             PetscInt dof;
7840 
7841             PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7842             if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7843               PetscInt                          nRows = newPointOffsets[f][p + 1] - rStart;
7844               const PetscScalar *PETSC_RESTRICT mat   = pointMat[f] + pointMatOffsets[f][p];
7845 
7846               for (r = 0; r < nRows; r++) {
7847                 for (c = 0; c < newNumIndices; c++) {
7848                   for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7849                 }
7850               }
7851             } else {
7852               /* copy this row as is */
7853               for (r = 0; r < dof; r++) {
7854                 for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7855               }
7856             }
7857             oldOff += dof;
7858           }
7859         }
7860       } else {
7861         PetscInt oldOff = 0;
7862 
7863         for (p = 0; p < numPoints; p++) {
7864           PetscInt rStart = newPointOffsets[0][p];
7865           PetscInt b      = points[2 * p];
7866           PetscInt c, r, k;
7867           PetscInt dof;
7868 
7869           PetscCall(PetscSectionGetDof(section, b, &dof));
7870           if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7871             PetscInt                          nRows = newPointOffsets[0][p + 1] - rStart;
7872             const PetscScalar *PETSC_RESTRICT mat   = pointMat[0] + pointMatOffsets[0][p];
7873 
7874             for (r = 0; r < nRows; r++) {
7875               for (c = 0; c < newNumIndices; c++) {
7876                 for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7877               }
7878             }
7879           } else {
7880             /* copy this row as is */
7881             for (r = 0; r < dof; r++) {
7882               for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7883             }
7884           }
7885           oldOff += dof;
7886         }
7887       }
7888 
7889       PetscCall(DMRestoreWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7890     } else {
7891       newValues = tmpValues;
7892     }
7893   }
7894 
7895   /* clean up */
7896   PetscCall(DMRestoreWorkArray(dm, maxDof, MPIU_INT, &indices));
7897   PetscCall(DMRestoreWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7898 
7899   if (numFields) {
7900     for (f = 0; f < numFields; f++) {
7901       PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7902       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7903       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7904     }
7905   } else {
7906     PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7907     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7908     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[0]));
7909   }
7910   PetscCall(ISRestoreIndices(aIS, &anchors));
7911 
7912   /* output */
7913   if (outPoints) {
7914     *outPoints = newPoints;
7915   } else {
7916     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7917   }
7918   if (outValues) *outValues = newValues;
7919   for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7920   PetscFunctionReturn(PETSC_SUCCESS);
7921 }
7922 
7923 /*@C
7924   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
7925 
7926   Not collective
7927 
7928   Input Parameters:
7929 + dm         - The `DM`
7930 . section    - The `PetscSection` describing the points (a local section)
7931 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
7932 . point      - The point defining the closure
7933 - useClPerm  - Use the closure point permutation if available
7934 
7935   Output Parameters:
7936 + numIndices - The number of dof indices in the closure of point with the input sections
7937 . indices    - The dof indices
7938 . outOffsets - Array to write the field offsets into, or `NULL`
7939 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
7940 
7941   Level: advanced
7942 
7943   Notes:
7944   Must call `DMPlexRestoreClosureIndices()` to free allocated memory
7945 
7946   If `idxSection` is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
7947   of those indices is not significant.  If `idxSection` is local, the constrained dofs will yield the involution -(idx+1)
7948   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7949   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when `idxSection` == section, otherwise global
7950   indices (with the above semantics) are implied.
7951 
7952 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
7953           `PetscSection`, `DMGetGlobalSection()`
7954 @*/
7955 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7956 {
7957   /* Closure ordering */
7958   PetscSection    clSection;
7959   IS              clPoints;
7960   const PetscInt *clp;
7961   PetscInt       *points;
7962   const PetscInt *clperm = NULL;
7963   /* Dof permutation and sign flips */
7964   const PetscInt    **perms[32] = {NULL};
7965   const PetscScalar **flips[32] = {NULL};
7966   PetscScalar        *valCopy   = NULL;
7967   /* Hanging node constraints */
7968   PetscInt    *pointsC = NULL;
7969   PetscScalar *valuesC = NULL;
7970   PetscInt     NclC, NiC;
7971 
7972   PetscInt *idx;
7973   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7974   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7975 
7976   PetscFunctionBeginHot;
7977   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7978   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7979   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7980   if (numIndices) PetscAssertPointer(numIndices, 6);
7981   if (indices) PetscAssertPointer(indices, 7);
7982   if (outOffsets) PetscAssertPointer(outOffsets, 8);
7983   if (values) PetscAssertPointer(values, 9);
7984   PetscCall(PetscSectionGetNumFields(section, &Nf));
7985   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7986   PetscCall(PetscArrayzero(offsets, 32));
7987   /* 1) Get points in closure */
7988   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7989   if (useClPerm) {
7990     PetscInt depth, clsize;
7991     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7992     for (clsize = 0, p = 0; p < Ncl; p++) {
7993       PetscInt dof;
7994       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7995       clsize += dof;
7996     }
7997     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7998   }
7999   /* 2) Get number of indices on these points and field offsets from section */
8000   for (p = 0; p < Ncl * 2; p += 2) {
8001     PetscInt dof, fdof;
8002 
8003     PetscCall(PetscSectionGetDof(section, points[p], &dof));
8004     for (f = 0; f < Nf; ++f) {
8005       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
8006       offsets[f + 1] += fdof;
8007     }
8008     Ni += dof;
8009   }
8010   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
8011   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
8012   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
8013   for (f = 0; f < PetscMax(1, Nf); ++f) {
8014     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8015     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
8016     /* may need to apply sign changes to the element matrix */
8017     if (values && flips[f]) {
8018       PetscInt foffset = offsets[f];
8019 
8020       for (p = 0; p < Ncl; ++p) {
8021         PetscInt           pnt  = points[2 * p], fdof;
8022         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
8023 
8024         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
8025         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
8026         if (flip) {
8027           PetscInt i, j, k;
8028 
8029           if (!valCopy) {
8030             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8031             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
8032             *values = valCopy;
8033           }
8034           for (i = 0; i < fdof; ++i) {
8035             PetscScalar fval = flip[i];
8036 
8037             for (k = 0; k < Ni; ++k) {
8038               valCopy[Ni * (foffset + i) + k] *= fval;
8039               valCopy[Ni * k + (foffset + i)] *= fval;
8040             }
8041           }
8042         }
8043         foffset += fdof;
8044       }
8045     }
8046   }
8047   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
8048   PetscCall(DMPlexAnchorsModifyMat(dm, section, Ncl, Ni, points, perms, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, PETSC_TRUE));
8049   if (NclC) {
8050     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8051     for (f = 0; f < PetscMax(1, Nf); ++f) {
8052       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8053       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8054     }
8055     for (f = 0; f < PetscMax(1, Nf); ++f) {
8056       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
8057       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
8058     }
8059     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8060     Ncl    = NclC;
8061     Ni     = NiC;
8062     points = pointsC;
8063     if (values) *values = valuesC;
8064   }
8065   /* 5) Calculate indices */
8066   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
8067   if (Nf) {
8068     PetscInt  idxOff;
8069     PetscBool useFieldOffsets;
8070 
8071     if (outOffsets) {
8072       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
8073     }
8074     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
8075     if (useFieldOffsets) {
8076       for (p = 0; p < Ncl; ++p) {
8077         const PetscInt pnt = points[p * 2];
8078 
8079         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
8080       }
8081     } else {
8082       for (p = 0; p < Ncl; ++p) {
8083         const PetscInt pnt = points[p * 2];
8084 
8085         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8086         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8087          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
8088          * global section. */
8089         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
8090       }
8091     }
8092   } else {
8093     PetscInt off = 0, idxOff;
8094 
8095     for (p = 0; p < Ncl; ++p) {
8096       const PetscInt  pnt  = points[p * 2];
8097       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
8098 
8099       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8100       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8101        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
8102       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
8103     }
8104   }
8105   /* 6) Cleanup */
8106   for (f = 0; f < PetscMax(1, Nf); ++f) {
8107     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8108     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8109   }
8110   if (NclC) {
8111     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
8112   } else {
8113     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8114   }
8115 
8116   if (numIndices) *numIndices = Ni;
8117   if (indices) *indices = idx;
8118   PetscFunctionReturn(PETSC_SUCCESS);
8119 }
8120 
8121 /*@C
8122   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
8123 
8124   Not collective
8125 
8126   Input Parameters:
8127 + dm         - The `DM`
8128 . section    - The `PetscSection` describing the points (a local section)
8129 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8130 . point      - The point defining the closure
8131 - useClPerm  - Use the closure point permutation if available
8132 
8133   Output Parameters:
8134 + numIndices - The number of dof indices in the closure of point with the input sections
8135 . indices    - The dof indices
8136 . outOffsets - Array to write the field offsets into, or `NULL`
8137 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8138 
8139   Level: advanced
8140 
8141   Notes:
8142   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
8143 
8144   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8145   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
8146   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8147   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
8148   indices (with the above semantics) are implied.
8149 
8150 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
8151 @*/
8152 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
8153 {
8154   PetscFunctionBegin;
8155   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8156   PetscAssertPointer(indices, 7);
8157   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
8158   PetscFunctionReturn(PETSC_SUCCESS);
8159 }
8160 
8161 PetscErrorCode DMPlexMatSetClosure_Internal(DM dm, PetscSection section, PetscSection globalSection, PetscBool useClPerm, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8162 {
8163   DM_Plex           *mesh = (DM_Plex *)dm->data;
8164   PetscInt          *indices;
8165   PetscInt           numIndices;
8166   const PetscScalar *valuesOrig = values;
8167   PetscErrorCode     ierr;
8168 
8169   PetscFunctionBegin;
8170   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8171   if (!section) PetscCall(DMGetLocalSection(dm, &section));
8172   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
8173   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
8174   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
8175   PetscValidHeaderSpecific(A, MAT_CLASSID, 5);
8176 
8177   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, useClPerm, &numIndices, &indices, NULL, (PetscScalar **)&values));
8178 
8179   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
8180   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8181   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
8182   if (ierr) {
8183     PetscMPIInt rank;
8184 
8185     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8186     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8187     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
8188     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8189     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8190     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
8191   }
8192   if (mesh->printFEM > 1) {
8193     PetscInt i;
8194     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
8195     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
8196     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8197   }
8198 
8199   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8200   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8201   PetscFunctionReturn(PETSC_SUCCESS);
8202 }
8203 
8204 /*@C
8205   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
8206 
8207   Not collective
8208 
8209   Input Parameters:
8210 + dm            - The `DM`
8211 . section       - The section describing the layout in `v`, or `NULL` to use the default section
8212 . globalSection - The section describing the layout in `v`, or `NULL` to use the default global section
8213 . A             - The matrix
8214 . point         - The point in the `DM`
8215 . values        - The array of values
8216 - mode          - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8217 
8218   Level: intermediate
8219 
8220 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8221 @*/
8222 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8223 {
8224   PetscFunctionBegin;
8225   PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, PETSC_TRUE, A, point, values, mode));
8226   PetscFunctionReturn(PETSC_SUCCESS);
8227 }
8228 
8229 /*@C
8230   DMPlexMatSetClosureGeneral - Set an array of the values on the closure of 'point' using a different row and column section
8231 
8232   Not collective
8233 
8234   Input Parameters:
8235 + dmRow            - The `DM` for the row fields
8236 . sectionRow       - The section describing the layout, or `NULL` to use the default section in `dmRow`
8237 . useRowPerm       - The flag to use the closure permutation of the `dmRow` if available
8238 . globalSectionRow - The section describing the layout, or `NULL` to use the default global section in `dmRow`
8239 . dmCol            - The `DM` for the column fields
8240 . sectionCol       - The section describing the layout, or `NULL` to use the default section in `dmCol`
8241 . useColPerm       - The flag to use the closure permutation of the `dmCol` if available
8242 . globalSectionCol - The section describing the layout, or `NULL` to use the default global section in `dmCol`
8243 . A                - The matrix
8244 . point            - The point in the `DM`
8245 . values           - The array of values
8246 - mode             - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8247 
8248   Level: intermediate
8249 
8250 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8251 @*/
8252 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)
8253 {
8254   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
8255   PetscInt          *indicesRow, *indicesCol;
8256   PetscInt           numIndicesRow, numIndicesCol;
8257   const PetscScalar *valuesOrig = values;
8258   PetscErrorCode     ierr;
8259 
8260   PetscFunctionBegin;
8261   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
8262   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
8263   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
8264   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
8265   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
8266   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 5);
8267   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
8268   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 6);
8269   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
8270   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 7);
8271   PetscValidHeaderSpecific(A, MAT_CLASSID, 9);
8272 
8273   PetscCall(DMPlexGetClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
8274   PetscCall(DMPlexGetClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
8275 
8276   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
8277   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8278   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values, mode);
8279   if (ierr) {
8280     PetscMPIInt rank;
8281 
8282     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8283     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8284     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
8285     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
8286     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&values));
8287     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
8288   }
8289 
8290   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
8291   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
8292   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
8293   PetscFunctionReturn(PETSC_SUCCESS);
8294 }
8295 
8296 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8297 {
8298   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
8299   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8300   PetscInt       *cpoints = NULL;
8301   PetscInt       *findices, *cindices;
8302   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8303   PetscInt        foffsets[32], coffsets[32];
8304   DMPolytopeType  ct;
8305   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8306   PetscErrorCode  ierr;
8307 
8308   PetscFunctionBegin;
8309   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8310   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8311   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8312   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8313   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8314   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8315   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8316   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8317   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8318   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8319   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
8320   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8321   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8322   PetscCall(PetscArrayzero(foffsets, 32));
8323   PetscCall(PetscArrayzero(coffsets, 32));
8324   /* Column indices */
8325   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8326   maxFPoints = numCPoints;
8327   /* Compress out points not in the section */
8328   /*   TODO: Squeeze out points with 0 dof as well */
8329   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8330   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8331     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8332       cpoints[q * 2]     = cpoints[p];
8333       cpoints[q * 2 + 1] = cpoints[p + 1];
8334       ++q;
8335     }
8336   }
8337   numCPoints = q;
8338   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8339     PetscInt fdof;
8340 
8341     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8342     if (!dof) continue;
8343     for (f = 0; f < numFields; ++f) {
8344       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8345       coffsets[f + 1] += fdof;
8346     }
8347     numCIndices += dof;
8348   }
8349   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8350   /* Row indices */
8351   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8352   {
8353     DMPlexTransform tr;
8354     DMPolytopeType *rct;
8355     PetscInt       *rsize, *rcone, *rornt, Nt;
8356 
8357     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8358     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8359     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8360     numSubcells = rsize[Nt - 1];
8361     PetscCall(DMPlexTransformDestroy(&tr));
8362   }
8363   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8364   for (r = 0, q = 0; r < numSubcells; ++r) {
8365     /* TODO Map from coarse to fine cells */
8366     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8367     /* Compress out points not in the section */
8368     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8369     for (p = 0; p < numFPoints * 2; p += 2) {
8370       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8371         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8372         if (!dof) continue;
8373         for (s = 0; s < q; ++s)
8374           if (fpoints[p] == ftotpoints[s * 2]) break;
8375         if (s < q) continue;
8376         ftotpoints[q * 2]     = fpoints[p];
8377         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8378         ++q;
8379       }
8380     }
8381     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8382   }
8383   numFPoints = q;
8384   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8385     PetscInt fdof;
8386 
8387     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8388     if (!dof) continue;
8389     for (f = 0; f < numFields; ++f) {
8390       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8391       foffsets[f + 1] += fdof;
8392     }
8393     numFIndices += dof;
8394   }
8395   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8396 
8397   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8398   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8399   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8400   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8401   if (numFields) {
8402     const PetscInt **permsF[32] = {NULL};
8403     const PetscInt **permsC[32] = {NULL};
8404 
8405     for (f = 0; f < numFields; f++) {
8406       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8407       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8408     }
8409     for (p = 0; p < numFPoints; p++) {
8410       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8411       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8412     }
8413     for (p = 0; p < numCPoints; p++) {
8414       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8415       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8416     }
8417     for (f = 0; f < numFields; f++) {
8418       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8419       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8420     }
8421   } else {
8422     const PetscInt **permsF = NULL;
8423     const PetscInt **permsC = NULL;
8424 
8425     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8426     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8427     for (p = 0, off = 0; p < numFPoints; p++) {
8428       const PetscInt *perm = permsF ? permsF[p] : NULL;
8429 
8430       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8431       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8432     }
8433     for (p = 0, off = 0; p < numCPoints; p++) {
8434       const PetscInt *perm = permsC ? permsC[p] : NULL;
8435 
8436       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8437       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8438     }
8439     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8440     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8441   }
8442   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8443   /* TODO: flips */
8444   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8445   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8446   if (ierr) {
8447     PetscMPIInt rank;
8448 
8449     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8450     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8451     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8452     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8453     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8454   }
8455   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8456   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8457   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8458   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8459   PetscFunctionReturn(PETSC_SUCCESS);
8460 }
8461 
8462 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8463 {
8464   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8465   PetscInt       *cpoints      = NULL;
8466   PetscInt        foffsets[32] = {0}, coffsets[32] = {0};
8467   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8468   DMPolytopeType  ct;
8469   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8470 
8471   PetscFunctionBegin;
8472   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8473   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8474   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8475   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8476   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8477   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8478   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8479   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8480   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8481   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8482   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8483   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8484   /* Column indices */
8485   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8486   maxFPoints = numCPoints;
8487   /* Compress out points not in the section */
8488   /*   TODO: Squeeze out points with 0 dof as well */
8489   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8490   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8491     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8492       cpoints[q * 2]     = cpoints[p];
8493       cpoints[q * 2 + 1] = cpoints[p + 1];
8494       ++q;
8495     }
8496   }
8497   numCPoints = q;
8498   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8499     PetscInt fdof;
8500 
8501     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8502     if (!dof) continue;
8503     for (f = 0; f < numFields; ++f) {
8504       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8505       coffsets[f + 1] += fdof;
8506     }
8507     numCIndices += dof;
8508   }
8509   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8510   /* Row indices */
8511   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8512   {
8513     DMPlexTransform tr;
8514     DMPolytopeType *rct;
8515     PetscInt       *rsize, *rcone, *rornt, Nt;
8516 
8517     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8518     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8519     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8520     numSubcells = rsize[Nt - 1];
8521     PetscCall(DMPlexTransformDestroy(&tr));
8522   }
8523   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8524   for (r = 0, q = 0; r < numSubcells; ++r) {
8525     /* TODO Map from coarse to fine cells */
8526     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8527     /* Compress out points not in the section */
8528     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8529     for (p = 0; p < numFPoints * 2; p += 2) {
8530       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8531         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8532         if (!dof) continue;
8533         for (s = 0; s < q; ++s)
8534           if (fpoints[p] == ftotpoints[s * 2]) break;
8535         if (s < q) continue;
8536         ftotpoints[q * 2]     = fpoints[p];
8537         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8538         ++q;
8539       }
8540     }
8541     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8542   }
8543   numFPoints = q;
8544   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8545     PetscInt fdof;
8546 
8547     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8548     if (!dof) continue;
8549     for (f = 0; f < numFields; ++f) {
8550       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8551       foffsets[f + 1] += fdof;
8552     }
8553     numFIndices += dof;
8554   }
8555   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8556 
8557   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8558   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8559   if (numFields) {
8560     const PetscInt **permsF[32] = {NULL};
8561     const PetscInt **permsC[32] = {NULL};
8562 
8563     for (f = 0; f < numFields; f++) {
8564       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8565       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8566     }
8567     for (p = 0; p < numFPoints; p++) {
8568       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8569       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8570     }
8571     for (p = 0; p < numCPoints; p++) {
8572       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8573       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8574     }
8575     for (f = 0; f < numFields; f++) {
8576       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8577       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8578     }
8579   } else {
8580     const PetscInt **permsF = NULL;
8581     const PetscInt **permsC = NULL;
8582 
8583     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8584     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8585     for (p = 0, off = 0; p < numFPoints; p++) {
8586       const PetscInt *perm = permsF ? permsF[p] : NULL;
8587 
8588       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8589       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8590     }
8591     for (p = 0, off = 0; p < numCPoints; p++) {
8592       const PetscInt *perm = permsC ? permsC[p] : NULL;
8593 
8594       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8595       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8596     }
8597     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8598     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8599   }
8600   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8601   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8602   PetscFunctionReturn(PETSC_SUCCESS);
8603 }
8604 
8605 /*@C
8606   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8607 
8608   Input Parameter:
8609 . dm - The `DMPLEX` object
8610 
8611   Output Parameter:
8612 . cellHeight - The height of a cell
8613 
8614   Level: developer
8615 
8616 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetVTKCellHeight()`
8617 @*/
8618 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8619 {
8620   DM_Plex *mesh = (DM_Plex *)dm->data;
8621 
8622   PetscFunctionBegin;
8623   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8624   PetscAssertPointer(cellHeight, 2);
8625   *cellHeight = mesh->vtkCellHeight;
8626   PetscFunctionReturn(PETSC_SUCCESS);
8627 }
8628 
8629 /*@C
8630   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8631 
8632   Input Parameters:
8633 + dm         - The `DMPLEX` object
8634 - cellHeight - The height of a cell
8635 
8636   Level: developer
8637 
8638 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8639 @*/
8640 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8641 {
8642   DM_Plex *mesh = (DM_Plex *)dm->data;
8643 
8644   PetscFunctionBegin;
8645   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8646   mesh->vtkCellHeight = cellHeight;
8647   PetscFunctionReturn(PETSC_SUCCESS);
8648 }
8649 
8650 /*@
8651   DMPlexGetCellTypeStratum - Get the range of cells of a given celltype
8652 
8653   Input Parameters:
8654 + dm - The `DMPLEX` object
8655 - ct - The `DMPolytopeType` of the cell
8656 
8657   Output Parameters:
8658 + start - The first cell of this type, or `NULL`
8659 - end   - The upper bound on this celltype, or `NULL`
8660 
8661   Level: advanced
8662 
8663 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8664 @*/
8665 PetscErrorCode DMPlexGetCellTypeStratum(DM dm, DMPolytopeType ct, PetscInt *start, PetscInt *end)
8666 {
8667   DM_Plex *mesh = (DM_Plex *)dm->data;
8668   DMLabel  label;
8669   PetscInt pStart, pEnd;
8670 
8671   PetscFunctionBegin;
8672   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8673   if (start) {
8674     PetscAssertPointer(start, 3);
8675     *start = 0;
8676   }
8677   if (end) {
8678     PetscAssertPointer(end, 4);
8679     *end = 0;
8680   }
8681   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8682   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
8683   if (mesh->tr) {
8684     PetscCall(DMPlexTransformGetCellTypeStratum(mesh->tr, ct, start, end));
8685   } else {
8686     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
8687     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named celltype was found");
8688     PetscCall(DMLabelGetStratumBounds(label, ct, start, end));
8689   }
8690   PetscFunctionReturn(PETSC_SUCCESS);
8691 }
8692 
8693 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8694 {
8695   PetscSection section, globalSection;
8696   PetscInt    *numbers, p;
8697 
8698   PetscFunctionBegin;
8699   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8700   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8701   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8702   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8703   PetscCall(PetscSectionSetUp(section));
8704   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &globalSection));
8705   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8706   for (p = pStart; p < pEnd; ++p) {
8707     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8708     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8709     else numbers[p - pStart] += shift;
8710   }
8711   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8712   if (globalSize) {
8713     PetscLayout layout;
8714     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8715     PetscCall(PetscLayoutGetSize(layout, globalSize));
8716     PetscCall(PetscLayoutDestroy(&layout));
8717   }
8718   PetscCall(PetscSectionDestroy(&section));
8719   PetscCall(PetscSectionDestroy(&globalSection));
8720   PetscFunctionReturn(PETSC_SUCCESS);
8721 }
8722 
8723 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
8724 {
8725   PetscInt cellHeight, cStart, cEnd;
8726 
8727   PetscFunctionBegin;
8728   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8729   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8730   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8731   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8732   PetscFunctionReturn(PETSC_SUCCESS);
8733 }
8734 
8735 /*@
8736   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8737 
8738   Input Parameter:
8739 . dm - The `DMPLEX` object
8740 
8741   Output Parameter:
8742 . globalCellNumbers - Global cell numbers for all cells on this process
8743 
8744   Level: developer
8745 
8746 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVertexNumbering()`
8747 @*/
8748 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8749 {
8750   DM_Plex *mesh = (DM_Plex *)dm->data;
8751 
8752   PetscFunctionBegin;
8753   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8754   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8755   *globalCellNumbers = mesh->globalCellNumbers;
8756   PetscFunctionReturn(PETSC_SUCCESS);
8757 }
8758 
8759 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8760 {
8761   PetscInt vStart, vEnd;
8762 
8763   PetscFunctionBegin;
8764   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8765   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8766   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8767   PetscFunctionReturn(PETSC_SUCCESS);
8768 }
8769 
8770 /*@
8771   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8772 
8773   Input Parameter:
8774 . dm - The `DMPLEX` object
8775 
8776   Output Parameter:
8777 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8778 
8779   Level: developer
8780 
8781 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8782 @*/
8783 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8784 {
8785   DM_Plex *mesh = (DM_Plex *)dm->data;
8786 
8787   PetscFunctionBegin;
8788   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8789   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8790   *globalVertexNumbers = mesh->globalVertexNumbers;
8791   PetscFunctionReturn(PETSC_SUCCESS);
8792 }
8793 
8794 /*@
8795   DMPlexCreatePointNumbering - Create a global numbering for all points.
8796 
8797   Collective
8798 
8799   Input Parameter:
8800 . dm - The `DMPLEX` object
8801 
8802   Output Parameter:
8803 . globalPointNumbers - Global numbers for all points on this process
8804 
8805   Level: developer
8806 
8807   Notes:
8808   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8809   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8810   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8811   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8812 
8813   The partitioned mesh is
8814   ```
8815   (2)--0--(3)--1--(4)    (1)--0--(2)
8816   ```
8817   and its global numbering is
8818   ```
8819   (3)--0--(4)--1--(5)--2--(6)
8820   ```
8821   Then the global numbering is provided as
8822   ```
8823   [0] Number of indices in set 5
8824   [0] 0 0
8825   [0] 1 1
8826   [0] 2 3
8827   [0] 3 4
8828   [0] 4 -6
8829   [1] Number of indices in set 3
8830   [1] 0 2
8831   [1] 1 5
8832   [1] 2 6
8833   ```
8834 
8835 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8836 @*/
8837 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8838 {
8839   IS        nums[4];
8840   PetscInt  depths[4], gdepths[4], starts[4];
8841   PetscInt  depth, d, shift = 0;
8842   PetscBool empty = PETSC_FALSE;
8843 
8844   PetscFunctionBegin;
8845   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8846   PetscCall(DMPlexGetDepth(dm, &depth));
8847   // For unstratified meshes use dim instead of depth
8848   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8849   // If any stratum is empty, we must mark all empty
8850   for (d = 0; d <= depth; ++d) {
8851     PetscInt end;
8852 
8853     depths[d] = depth - d;
8854     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8855     if (!(starts[d] - end)) empty = PETSC_TRUE;
8856   }
8857   if (empty)
8858     for (d = 0; d <= depth; ++d) {
8859       depths[d] = -1;
8860       starts[d] = -1;
8861     }
8862   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8863   PetscCall(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8864   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]);
8865   // Note here that 'shift' is collective, so that the numbering is stratified by depth
8866   for (d = 0; d <= depth; ++d) {
8867     PetscInt pStart, pEnd, gsize;
8868 
8869     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8870     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8871     shift += gsize;
8872   }
8873   PetscCall(ISConcatenate(PETSC_COMM_SELF, depth + 1, nums, globalPointNumbers));
8874   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8875   PetscFunctionReturn(PETSC_SUCCESS);
8876 }
8877 
8878 /*@
8879   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8880 
8881   Input Parameter:
8882 . dm - The `DMPLEX` object
8883 
8884   Output Parameter:
8885 . ranks - The rank field
8886 
8887   Options Database Key:
8888 . -dm_partition_view - Adds the rank field into the `DM` output from `-dm_view` using the same viewer
8889 
8890   Level: intermediate
8891 
8892 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8893 @*/
8894 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8895 {
8896   DM             rdm;
8897   PetscFE        fe;
8898   PetscScalar   *r;
8899   PetscMPIInt    rank;
8900   DMPolytopeType ct;
8901   PetscInt       dim, cStart, cEnd, c;
8902   PetscBool      simplex;
8903 
8904   PetscFunctionBeginUser;
8905   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8906   PetscAssertPointer(ranks, 2);
8907   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
8908   PetscCall(DMClone(dm, &rdm));
8909   PetscCall(DMGetDimension(rdm, &dim));
8910   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8911   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8912   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
8913   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8914   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
8915   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8916   PetscCall(PetscFEDestroy(&fe));
8917   PetscCall(DMCreateDS(rdm));
8918   PetscCall(DMCreateGlobalVector(rdm, ranks));
8919   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
8920   PetscCall(VecGetArray(*ranks, &r));
8921   for (c = cStart; c < cEnd; ++c) {
8922     PetscScalar *lr;
8923 
8924     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8925     if (lr) *lr = rank;
8926   }
8927   PetscCall(VecRestoreArray(*ranks, &r));
8928   PetscCall(DMDestroy(&rdm));
8929   PetscFunctionReturn(PETSC_SUCCESS);
8930 }
8931 
8932 /*@
8933   DMPlexCreateLabelField - Create a field whose value is the label value for that point
8934 
8935   Input Parameters:
8936 + dm    - The `DMPLEX`
8937 - label - The `DMLabel`
8938 
8939   Output Parameter:
8940 . val - The label value field
8941 
8942   Options Database Key:
8943 . -dm_label_view - Adds the label value field into the `DM` output from `-dm_view` using the same viewer
8944 
8945   Level: intermediate
8946 
8947 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8948 @*/
8949 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
8950 {
8951   DM             rdm, plex;
8952   Vec            lval;
8953   PetscSection   section;
8954   PetscFE        fe;
8955   PetscScalar   *v;
8956   PetscInt       dim, pStart, pEnd, p, cStart;
8957   DMPolytopeType ct;
8958   char           name[PETSC_MAX_PATH_LEN];
8959   const char    *lname, *prefix;
8960 
8961   PetscFunctionBeginUser;
8962   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8963   PetscAssertPointer(label, 2);
8964   PetscAssertPointer(val, 3);
8965   PetscCall(DMClone(dm, &rdm));
8966   PetscCall(DMConvert(rdm, DMPLEX, &plex));
8967   PetscCall(DMPlexGetHeightStratum(plex, 0, &cStart, NULL));
8968   PetscCall(DMPlexGetCellType(plex, cStart, &ct));
8969   PetscCall(DMDestroy(&plex));
8970   PetscCall(DMGetDimension(rdm, &dim));
8971   PetscCall(DMGetOptionsPrefix(dm, &prefix));
8972   PetscCall(PetscObjectGetName((PetscObject)label, &lname));
8973   PetscCall(PetscSNPrintf(name, sizeof(name), "%s%s_", prefix ? prefix : "", lname));
8974   PetscCall(PetscFECreateByCell(PETSC_COMM_SELF, dim, 1, ct, name, -1, &fe));
8975   PetscCall(PetscObjectSetName((PetscObject)fe, ""));
8976   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8977   PetscCall(PetscFEDestroy(&fe));
8978   PetscCall(DMCreateDS(rdm));
8979   PetscCall(DMCreateGlobalVector(rdm, val));
8980   PetscCall(DMCreateLocalVector(rdm, &lval));
8981   PetscCall(PetscObjectSetName((PetscObject)*val, lname));
8982   PetscCall(DMGetLocalSection(rdm, &section));
8983   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
8984   PetscCall(VecGetArray(lval, &v));
8985   for (p = pStart; p < pEnd; ++p) {
8986     PetscInt cval, dof, off;
8987 
8988     PetscCall(PetscSectionGetDof(section, p, &dof));
8989     if (!dof) continue;
8990     PetscCall(DMLabelGetValue(label, p, &cval));
8991     PetscCall(PetscSectionGetOffset(section, p, &off));
8992     for (PetscInt d = 0; d < dof; d++) v[off + d] = cval;
8993   }
8994   PetscCall(VecRestoreArray(lval, &v));
8995   PetscCall(DMLocalToGlobal(rdm, lval, INSERT_VALUES, *val));
8996   PetscCall(VecDestroy(&lval));
8997   PetscCall(DMDestroy(&rdm));
8998   PetscFunctionReturn(PETSC_SUCCESS);
8999 }
9000 
9001 /*@
9002   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
9003 
9004   Input Parameter:
9005 . dm - The `DMPLEX` object
9006 
9007   Level: developer
9008 
9009   Notes:
9010   This is a useful diagnostic when creating meshes programmatically.
9011 
9012   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9013 
9014 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9015 @*/
9016 PetscErrorCode DMPlexCheckSymmetry(DM dm)
9017 {
9018   PetscSection    coneSection, supportSection;
9019   const PetscInt *cone, *support;
9020   PetscInt        coneSize, c, supportSize, s;
9021   PetscInt        pStart, pEnd, p, pp, csize, ssize;
9022   PetscBool       storagecheck = PETSC_TRUE;
9023 
9024   PetscFunctionBegin;
9025   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9026   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
9027   PetscCall(DMPlexGetConeSection(dm, &coneSection));
9028   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
9029   /* Check that point p is found in the support of its cone points, and vice versa */
9030   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9031   for (p = pStart; p < pEnd; ++p) {
9032     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
9033     PetscCall(DMPlexGetCone(dm, p, &cone));
9034     for (c = 0; c < coneSize; ++c) {
9035       PetscBool dup = PETSC_FALSE;
9036       PetscInt  d;
9037       for (d = c - 1; d >= 0; --d) {
9038         if (cone[c] == cone[d]) {
9039           dup = PETSC_TRUE;
9040           break;
9041         }
9042       }
9043       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
9044       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
9045       for (s = 0; s < supportSize; ++s) {
9046         if (support[s] == p) break;
9047       }
9048       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
9049         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
9050         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
9051         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9052         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
9053         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
9054         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9055         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]);
9056         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
9057       }
9058     }
9059     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
9060     if (p != pp) {
9061       storagecheck = PETSC_FALSE;
9062       continue;
9063     }
9064     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
9065     PetscCall(DMPlexGetSupport(dm, p, &support));
9066     for (s = 0; s < supportSize; ++s) {
9067       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
9068       PetscCall(DMPlexGetCone(dm, support[s], &cone));
9069       for (c = 0; c < coneSize; ++c) {
9070         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
9071         if (cone[c] != pp) {
9072           c = 0;
9073           break;
9074         }
9075         if (cone[c] == p) break;
9076       }
9077       if (c >= coneSize) {
9078         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
9079         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
9080         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9081         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
9082         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
9083         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9084         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
9085       }
9086     }
9087   }
9088   if (storagecheck) {
9089     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
9090     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
9091     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
9092   }
9093   PetscFunctionReturn(PETSC_SUCCESS);
9094 }
9095 
9096 /*
9097   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.
9098 */
9099 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
9100 {
9101   DMPolytopeType  cct;
9102   PetscInt        ptpoints[4];
9103   const PetscInt *cone, *ccone, *ptcone;
9104   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
9105 
9106   PetscFunctionBegin;
9107   *unsplit = 0;
9108   switch (ct) {
9109   case DM_POLYTOPE_POINT_PRISM_TENSOR:
9110     ptpoints[npt++] = c;
9111     break;
9112   case DM_POLYTOPE_SEG_PRISM_TENSOR:
9113     PetscCall(DMPlexGetCone(dm, c, &cone));
9114     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9115     for (cp = 0; cp < coneSize; ++cp) {
9116       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
9117       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
9118     }
9119     break;
9120   case DM_POLYTOPE_TRI_PRISM_TENSOR:
9121   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9122     PetscCall(DMPlexGetCone(dm, c, &cone));
9123     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9124     for (cp = 0; cp < coneSize; ++cp) {
9125       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
9126       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
9127       for (ccp = 0; ccp < cconeSize; ++ccp) {
9128         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
9129         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
9130           PetscInt p;
9131           for (p = 0; p < npt; ++p)
9132             if (ptpoints[p] == ccone[ccp]) break;
9133           if (p == npt) ptpoints[npt++] = ccone[ccp];
9134         }
9135       }
9136     }
9137     break;
9138   default:
9139     break;
9140   }
9141   for (pt = 0; pt < npt; ++pt) {
9142     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
9143     if (ptcone[0] == ptcone[1]) ++(*unsplit);
9144   }
9145   PetscFunctionReturn(PETSC_SUCCESS);
9146 }
9147 
9148 /*@
9149   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
9150 
9151   Input Parameters:
9152 + dm         - The `DMPLEX` object
9153 - cellHeight - Normally 0
9154 
9155   Level: developer
9156 
9157   Notes:
9158   This is a useful diagnostic when creating meshes programmatically.
9159   Currently applicable only to homogeneous simplex or tensor meshes.
9160 
9161   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9162 
9163 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9164 @*/
9165 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
9166 {
9167   DMPlexInterpolatedFlag interp;
9168   DMPolytopeType         ct;
9169   PetscInt               vStart, vEnd, cStart, cEnd, c;
9170 
9171   PetscFunctionBegin;
9172   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9173   PetscCall(DMPlexIsInterpolated(dm, &interp));
9174   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9175   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9176   for (c = cStart; c < cEnd; ++c) {
9177     PetscInt *closure = NULL;
9178     PetscInt  coneSize, closureSize, cl, Nv = 0;
9179 
9180     PetscCall(DMPlexGetCellType(dm, c, &ct));
9181     PetscCheck((PetscInt)ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
9182     if (ct == DM_POLYTOPE_UNKNOWN) continue;
9183     if (interp == DMPLEX_INTERPOLATED_FULL) {
9184       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9185       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));
9186     }
9187     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9188     for (cl = 0; cl < closureSize * 2; cl += 2) {
9189       const PetscInt p = closure[cl];
9190       if ((p >= vStart) && (p < vEnd)) ++Nv;
9191     }
9192     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9193     /* Special Case: Tensor faces with identified vertices */
9194     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
9195       PetscInt unsplit;
9196 
9197       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9198       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
9199     }
9200     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));
9201   }
9202   PetscFunctionReturn(PETSC_SUCCESS);
9203 }
9204 
9205 /*@
9206   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
9207 
9208   Collective
9209 
9210   Input Parameters:
9211 + dm         - The `DMPLEX` object
9212 - cellHeight - Normally 0
9213 
9214   Level: developer
9215 
9216   Notes:
9217   This is a useful diagnostic when creating meshes programmatically.
9218   This routine is only relevant for meshes that are fully interpolated across all ranks.
9219   It will error out if a partially interpolated mesh is given on some rank.
9220   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
9221 
9222   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9223 
9224 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
9225 @*/
9226 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
9227 {
9228   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
9229   DMPlexInterpolatedFlag interpEnum;
9230 
9231   PetscFunctionBegin;
9232   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9233   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
9234   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(PETSC_SUCCESS);
9235   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
9236     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported"));
9237     PetscFunctionReturn(PETSC_SUCCESS);
9238   }
9239 
9240   PetscCall(DMGetDimension(dm, &dim));
9241   PetscCall(DMPlexGetDepth(dm, &depth));
9242   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9243   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
9244     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
9245     for (c = cStart; c < cEnd; ++c) {
9246       const PetscInt       *cone, *ornt, *faceSizes, *faces;
9247       const DMPolytopeType *faceTypes;
9248       DMPolytopeType        ct;
9249       PetscInt              numFaces, coneSize, f;
9250       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
9251 
9252       PetscCall(DMPlexGetCellType(dm, c, &ct));
9253       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9254       if (unsplit) continue;
9255       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9256       PetscCall(DMPlexGetCone(dm, c, &cone));
9257       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
9258       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9259       for (cl = 0; cl < closureSize * 2; cl += 2) {
9260         const PetscInt p = closure[cl];
9261         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
9262       }
9263       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9264       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);
9265       for (f = 0; f < numFaces; ++f) {
9266         DMPolytopeType fct;
9267         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
9268 
9269         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
9270         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
9271         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
9272           const PetscInt p = fclosure[cl];
9273           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
9274         }
9275         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]);
9276         for (v = 0; v < fnumCorners; ++v) {
9277           if (fclosure[v] != faces[fOff + v]) {
9278             PetscInt v1;
9279 
9280             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
9281             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
9282             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
9283             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
9284             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9285             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]);
9286           }
9287         }
9288         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
9289         fOff += faceSizes[f];
9290       }
9291       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9292       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9293     }
9294   }
9295   PetscFunctionReturn(PETSC_SUCCESS);
9296 }
9297 
9298 /*@
9299   DMPlexCheckGeometry - Check the geometry of mesh cells
9300 
9301   Input Parameter:
9302 . dm - The `DMPLEX` object
9303 
9304   Level: developer
9305 
9306   Notes:
9307   This is a useful diagnostic when creating meshes programmatically.
9308 
9309   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9310 
9311 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9312 @*/
9313 PetscErrorCode DMPlexCheckGeometry(DM dm)
9314 {
9315   Vec       coordinates;
9316   PetscReal detJ, J[9], refVol = 1.0;
9317   PetscReal vol;
9318   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
9319 
9320   PetscFunctionBegin;
9321   PetscCall(DMGetDimension(dm, &dim));
9322   PetscCall(DMGetCoordinateDim(dm, &dE));
9323   if (dim != dE) PetscFunctionReturn(PETSC_SUCCESS);
9324   PetscCall(DMPlexGetDepth(dm, &depth));
9325   for (d = 0; d < dim; ++d) refVol *= 2.0;
9326   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9327   /* Make sure local coordinates are created, because that step is collective */
9328   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
9329   if (!coordinates) PetscFunctionReturn(PETSC_SUCCESS);
9330   for (c = cStart; c < cEnd; ++c) {
9331     DMPolytopeType ct;
9332     PetscInt       unsplit;
9333     PetscBool      ignoreZeroVol = PETSC_FALSE;
9334 
9335     PetscCall(DMPlexGetCellType(dm, c, &ct));
9336     switch (ct) {
9337     case DM_POLYTOPE_SEG_PRISM_TENSOR:
9338     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9339     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9340       ignoreZeroVol = PETSC_TRUE;
9341       break;
9342     default:
9343       break;
9344     }
9345     switch (ct) {
9346     case DM_POLYTOPE_TRI_PRISM:
9347     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9348     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9349     case DM_POLYTOPE_PYRAMID:
9350       continue;
9351     default:
9352       break;
9353     }
9354     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9355     if (unsplit) continue;
9356     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
9357     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);
9358     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
9359     /* This should work with periodicity since DG coordinates should be used */
9360     if (depth > 1) {
9361       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
9362       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);
9363       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
9364     }
9365   }
9366   PetscFunctionReturn(PETSC_SUCCESS);
9367 }
9368 
9369 /*@
9370   DMPlexCheckPointSF - Check that several necessary conditions are met for the point `PetscSF` of this plex.
9371 
9372   Collective
9373 
9374   Input Parameters:
9375 + dm              - The `DMPLEX` object
9376 . pointSF         - The `PetscSF`, or `NULL` for `PointSF` attached to `DM`
9377 - allowExtraRoots - Flag to allow extra points not present in the `DM`
9378 
9379   Level: developer
9380 
9381   Notes:
9382   This is mainly intended for debugging/testing purposes.
9383 
9384   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9385 
9386   Extra roots can come from periodic cuts, where additional points appear on the boundary
9387 
9388 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
9389 @*/
9390 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
9391 {
9392   PetscInt           l, nleaves, nroots, overlap;
9393   const PetscInt    *locals;
9394   const PetscSFNode *remotes;
9395   PetscBool          distributed;
9396   MPI_Comm           comm;
9397   PetscMPIInt        rank;
9398 
9399   PetscFunctionBegin;
9400   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9401   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
9402   else pointSF = dm->sf;
9403   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9404   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
9405   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9406   {
9407     PetscMPIInt mpiFlag;
9408 
9409     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
9410     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
9411   }
9412   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
9413   PetscCall(DMPlexIsDistributed(dm, &distributed));
9414   if (!distributed) {
9415     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);
9416     PetscFunctionReturn(PETSC_SUCCESS);
9417   }
9418   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);
9419   PetscCall(DMPlexGetOverlap(dm, &overlap));
9420 
9421   /* Check SF graph is compatible with DMPlex chart */
9422   {
9423     PetscInt pStart, pEnd, maxLeaf;
9424 
9425     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9426     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9427     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9428     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9429   }
9430 
9431   /* Check Point SF has no local points referenced */
9432   for (l = 0; l < nleaves; l++) {
9433     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);
9434   }
9435 
9436   /* Check there are no cells in interface */
9437   if (!overlap) {
9438     PetscInt cellHeight, cStart, cEnd;
9439 
9440     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9441     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9442     for (l = 0; l < nleaves; ++l) {
9443       const PetscInt point = locals ? locals[l] : l;
9444 
9445       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9446     }
9447   }
9448 
9449   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9450   {
9451     const PetscInt *rootdegree;
9452 
9453     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9454     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9455     for (l = 0; l < nleaves; ++l) {
9456       const PetscInt  point = locals ? locals[l] : l;
9457       const PetscInt *cone;
9458       PetscInt        coneSize, c, idx;
9459 
9460       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9461       PetscCall(DMPlexGetCone(dm, point, &cone));
9462       for (c = 0; c < coneSize; ++c) {
9463         if (!rootdegree[cone[c]]) {
9464           if (locals) {
9465             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9466           } else {
9467             idx = (cone[c] < nleaves) ? cone[c] : -1;
9468           }
9469           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9470         }
9471       }
9472     }
9473   }
9474   PetscFunctionReturn(PETSC_SUCCESS);
9475 }
9476 
9477 /*@
9478   DMPlexCheck - Perform various checks of `DMPLEX` sanity
9479 
9480   Input Parameter:
9481 . dm - The `DMPLEX` object
9482 
9483   Level: developer
9484 
9485   Notes:
9486   This is a useful diagnostic when creating meshes programmatically.
9487 
9488   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9489 
9490   Currently does not include `DMPlexCheckCellShape()`.
9491 
9492 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9493 @*/
9494 PetscErrorCode DMPlexCheck(DM dm)
9495 {
9496   PetscInt cellHeight;
9497 
9498   PetscFunctionBegin;
9499   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9500   PetscCall(DMPlexCheckSymmetry(dm));
9501   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9502   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9503   PetscCall(DMPlexCheckGeometry(dm));
9504   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9505   PetscCall(DMPlexCheckInterfaceCones(dm));
9506   PetscFunctionReturn(PETSC_SUCCESS);
9507 }
9508 
9509 typedef struct cell_stats {
9510   PetscReal min, max, sum, squaresum;
9511   PetscInt  count;
9512 } cell_stats_t;
9513 
9514 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9515 {
9516   PetscInt i, N = *len;
9517 
9518   for (i = 0; i < N; i++) {
9519     cell_stats_t *A = (cell_stats_t *)a;
9520     cell_stats_t *B = (cell_stats_t *)b;
9521 
9522     B->min = PetscMin(A->min, B->min);
9523     B->max = PetscMax(A->max, B->max);
9524     B->sum += A->sum;
9525     B->squaresum += A->squaresum;
9526     B->count += A->count;
9527   }
9528 }
9529 
9530 /*@
9531   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9532 
9533   Collective
9534 
9535   Input Parameters:
9536 + dm        - The `DMPLEX` object
9537 . output    - If true, statistics will be displayed on `stdout`
9538 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9539 
9540   Level: developer
9541 
9542   Notes:
9543   This is mainly intended for debugging/testing purposes.
9544 
9545   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9546 
9547 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9548 @*/
9549 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9550 {
9551   DM           dmCoarse;
9552   cell_stats_t stats, globalStats;
9553   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9554   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9555   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9556   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9557   PetscMPIInt  rank, size;
9558 
9559   PetscFunctionBegin;
9560   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9561   stats.min = PETSC_MAX_REAL;
9562   stats.max = PETSC_MIN_REAL;
9563   stats.sum = stats.squaresum = 0.;
9564   stats.count                 = 0;
9565 
9566   PetscCallMPI(MPI_Comm_size(comm, &size));
9567   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9568   PetscCall(DMGetCoordinateDim(dm, &cdim));
9569   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9570   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9571   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9572   for (c = cStart; c < cEnd; c++) {
9573     PetscInt  i;
9574     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9575 
9576     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9577     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9578     for (i = 0; i < PetscSqr(cdim); ++i) {
9579       frobJ += J[i] * J[i];
9580       frobInvJ += invJ[i] * invJ[i];
9581     }
9582     cond2 = frobJ * frobInvJ;
9583     cond  = PetscSqrtReal(cond2);
9584 
9585     stats.min = PetscMin(stats.min, cond);
9586     stats.max = PetscMax(stats.max, cond);
9587     stats.sum += cond;
9588     stats.squaresum += cond2;
9589     stats.count++;
9590     if (output && cond > limit) {
9591       PetscSection coordSection;
9592       Vec          coordsLocal;
9593       PetscScalar *coords = NULL;
9594       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9595 
9596       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9597       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9598       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9599       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9600       for (i = 0; i < Nv / cdim; ++i) {
9601         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9602         for (d = 0; d < cdim; ++d) {
9603           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9604           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9605         }
9606         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9607       }
9608       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9609       for (cl = 0; cl < clSize * 2; cl += 2) {
9610         const PetscInt edge = closure[cl];
9611 
9612         if ((edge >= eStart) && (edge < eEnd)) {
9613           PetscReal len;
9614 
9615           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9616           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9617         }
9618       }
9619       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9620       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9621     }
9622   }
9623   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9624 
9625   if (size > 1) {
9626     PetscMPIInt  blockLengths[2] = {4, 1};
9627     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9628     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9629     MPI_Op       statReduce;
9630 
9631     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9632     PetscCallMPI(MPI_Type_commit(&statType));
9633     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9634     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9635     PetscCallMPI(MPI_Op_free(&statReduce));
9636     PetscCallMPI(MPI_Type_free(&statType));
9637   } else {
9638     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9639   }
9640   if (rank == 0) {
9641     count = globalStats.count;
9642     min   = globalStats.min;
9643     max   = globalStats.max;
9644     mean  = globalStats.sum / globalStats.count;
9645     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9646   }
9647 
9648   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));
9649   PetscCall(PetscFree2(J, invJ));
9650 
9651   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9652   if (dmCoarse) {
9653     PetscBool isplex;
9654 
9655     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9656     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9657   }
9658   PetscFunctionReturn(PETSC_SUCCESS);
9659 }
9660 
9661 /*@
9662   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9663   orthogonal quality below given tolerance.
9664 
9665   Collective
9666 
9667   Input Parameters:
9668 + dm   - The `DMPLEX` object
9669 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9670 - atol - [0, 1] Absolute tolerance for tagging cells.
9671 
9672   Output Parameters:
9673 + OrthQual      - `Vec` containing orthogonal quality per cell
9674 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9675 
9676   Options Database Keys:
9677 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9678 - -dm_plex_orthogonal_quality_vec_view   - view OrthQual vector.
9679 
9680   Level: intermediate
9681 
9682   Notes:
9683   Orthogonal quality is given by the following formula\:
9684 
9685   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9686 
9687   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
9688   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9689   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9690   calculating the cosine of the angle between these vectors.
9691 
9692   Orthogonal quality ranges from 1 (best) to 0 (worst).
9693 
9694   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9695   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9696 
9697   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9698 
9699 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9700 @*/
9701 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9702 {
9703   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9704   PetscInt              *idx;
9705   PetscScalar           *oqVals;
9706   const PetscScalar     *cellGeomArr, *faceGeomArr;
9707   PetscReal             *ci, *fi, *Ai;
9708   MPI_Comm               comm;
9709   Vec                    cellgeom, facegeom;
9710   DM                     dmFace, dmCell;
9711   IS                     glob;
9712   ISLocalToGlobalMapping ltog;
9713   PetscViewer            vwr;
9714 
9715   PetscFunctionBegin;
9716   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9717   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9718   PetscAssertPointer(OrthQual, 4);
9719   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9720   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9721   PetscCall(DMGetDimension(dm, &nc));
9722   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9723   {
9724     DMPlexInterpolatedFlag interpFlag;
9725 
9726     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9727     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9728       PetscMPIInt rank;
9729 
9730       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9731       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9732     }
9733   }
9734   if (OrthQualLabel) {
9735     PetscAssertPointer(OrthQualLabel, 5);
9736     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9737     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9738   } else {
9739     *OrthQualLabel = NULL;
9740   }
9741   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9742   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9743   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
9744   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9745   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9746   PetscCall(VecCreate(comm, OrthQual));
9747   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9748   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9749   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9750   PetscCall(VecSetUp(*OrthQual));
9751   PetscCall(ISDestroy(&glob));
9752   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9753   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9754   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9755   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9756   PetscCall(VecGetDM(cellgeom, &dmCell));
9757   PetscCall(VecGetDM(facegeom, &dmFace));
9758   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9759   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
9760     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9761     PetscInt         cellarr[2], *adj = NULL;
9762     PetscScalar     *cArr, *fArr;
9763     PetscReal        minvalc = 1.0, minvalf = 1.0;
9764     PetscFVCellGeom *cg;
9765 
9766     idx[cellIter] = cell - cStart;
9767     cellarr[0]    = cell;
9768     /* Make indexing into cellGeom easier */
9769     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9770     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9771     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9772     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9773     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
9774       PetscInt         i;
9775       const PetscInt   neigh  = adj[cellneigh];
9776       PetscReal        normci = 0, normfi = 0, normai = 0;
9777       PetscFVCellGeom *cgneigh;
9778       PetscFVFaceGeom *fg;
9779 
9780       /* Don't count ourselves in the neighbor list */
9781       if (neigh == cell) continue;
9782       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9783       cellarr[1] = neigh;
9784       {
9785         PetscInt        numcovpts;
9786         const PetscInt *covpts;
9787 
9788         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9789         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9790         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9791       }
9792 
9793       /* Compute c_i, f_i and their norms */
9794       for (i = 0; i < nc; i++) {
9795         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9796         fi[i] = fg->centroid[i] - cg->centroid[i];
9797         Ai[i] = fg->normal[i];
9798         normci += PetscPowReal(ci[i], 2);
9799         normfi += PetscPowReal(fi[i], 2);
9800         normai += PetscPowReal(Ai[i], 2);
9801       }
9802       normci = PetscSqrtReal(normci);
9803       normfi = PetscSqrtReal(normfi);
9804       normai = PetscSqrtReal(normai);
9805 
9806       /* Normalize and compute for each face-cell-normal pair */
9807       for (i = 0; i < nc; i++) {
9808         ci[i] = ci[i] / normci;
9809         fi[i] = fi[i] / normfi;
9810         Ai[i] = Ai[i] / normai;
9811         /* PetscAbs because I don't know if normals are guaranteed to point out */
9812         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
9813         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
9814       }
9815       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
9816       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
9817     }
9818     PetscCall(PetscFree(adj));
9819     PetscCall(PetscFree2(cArr, fArr));
9820     /* Defer to cell if they're equal */
9821     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9822     if (OrthQualLabel) {
9823       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9824     }
9825   }
9826   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
9827   PetscCall(VecAssemblyBegin(*OrthQual));
9828   PetscCall(VecAssemblyEnd(*OrthQual));
9829   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9830   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9831   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9832   if (OrthQualLabel) {
9833     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9834   }
9835   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9836   PetscCall(PetscOptionsRestoreViewer(&vwr));
9837   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9838   PetscFunctionReturn(PETSC_SUCCESS);
9839 }
9840 
9841 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9842  * interpolator construction */
9843 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9844 {
9845   PetscSection section, newSection, gsection;
9846   PetscSF      sf;
9847   PetscBool    hasConstraints, ghasConstraints;
9848 
9849   PetscFunctionBegin;
9850   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9851   PetscAssertPointer(odm, 2);
9852   PetscCall(DMGetLocalSection(dm, &section));
9853   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9854   PetscCall(MPIU_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
9855   if (!ghasConstraints) {
9856     PetscCall(PetscObjectReference((PetscObject)dm));
9857     *odm = dm;
9858     PetscFunctionReturn(PETSC_SUCCESS);
9859   }
9860   PetscCall(DMClone(dm, odm));
9861   PetscCall(DMCopyFields(dm, *odm));
9862   PetscCall(DMGetLocalSection(*odm, &newSection));
9863   PetscCall(DMGetPointSF(*odm, &sf));
9864   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_TRUE, PETSC_FALSE, &gsection));
9865   PetscCall(DMSetGlobalSection(*odm, gsection));
9866   PetscCall(PetscSectionDestroy(&gsection));
9867   PetscFunctionReturn(PETSC_SUCCESS);
9868 }
9869 
9870 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9871 {
9872   DM        dmco, dmfo;
9873   Mat       interpo;
9874   Vec       rscale;
9875   Vec       cglobalo, clocal;
9876   Vec       fglobal, fglobalo, flocal;
9877   PetscBool regular;
9878 
9879   PetscFunctionBegin;
9880   PetscCall(DMGetFullDM(dmc, &dmco));
9881   PetscCall(DMGetFullDM(dmf, &dmfo));
9882   PetscCall(DMSetCoarseDM(dmfo, dmco));
9883   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9884   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9885   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9886   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9887   PetscCall(DMCreateLocalVector(dmc, &clocal));
9888   PetscCall(VecSet(cglobalo, 0.));
9889   PetscCall(VecSet(clocal, 0.));
9890   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9891   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9892   PetscCall(DMCreateLocalVector(dmf, &flocal));
9893   PetscCall(VecSet(fglobal, 0.));
9894   PetscCall(VecSet(fglobalo, 0.));
9895   PetscCall(VecSet(flocal, 0.));
9896   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9897   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9898   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9899   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9900   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9901   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9902   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9903   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9904   *shift = fglobal;
9905   PetscCall(VecDestroy(&flocal));
9906   PetscCall(VecDestroy(&fglobalo));
9907   PetscCall(VecDestroy(&clocal));
9908   PetscCall(VecDestroy(&cglobalo));
9909   PetscCall(VecDestroy(&rscale));
9910   PetscCall(MatDestroy(&interpo));
9911   PetscCall(DMDestroy(&dmfo));
9912   PetscCall(DMDestroy(&dmco));
9913   PetscFunctionReturn(PETSC_SUCCESS);
9914 }
9915 
9916 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
9917 {
9918   PetscObject shifto;
9919   Vec         shift;
9920 
9921   PetscFunctionBegin;
9922   if (!interp) {
9923     Vec rscale;
9924 
9925     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9926     PetscCall(VecDestroy(&rscale));
9927   } else {
9928     PetscCall(PetscObjectReference((PetscObject)interp));
9929   }
9930   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9931   if (!shifto) {
9932     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9933     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
9934     shifto = (PetscObject)shift;
9935     PetscCall(VecDestroy(&shift));
9936   }
9937   shift = (Vec)shifto;
9938   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9939   PetscCall(VecAXPY(fineSol, 1.0, shift));
9940   PetscCall(MatDestroy(&interp));
9941   PetscFunctionReturn(PETSC_SUCCESS);
9942 }
9943 
9944 /* Pointwise interpolation
9945      Just code FEM for now
9946      u^f = I u^c
9947      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9948      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9949      I_{ij} = psi^f_i phi^c_j
9950 */
9951 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
9952 {
9953   PetscSection gsc, gsf;
9954   PetscInt     m, n;
9955   void        *ctx;
9956   DM           cdm;
9957   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9958 
9959   PetscFunctionBegin;
9960   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9961   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9962   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9963   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9964 
9965   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9966   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
9967   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9968   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9969   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9970 
9971   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9972   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9973   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9974   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9975   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9976   if (scaling) {
9977     /* Use naive scaling */
9978     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
9979   }
9980   PetscFunctionReturn(PETSC_SUCCESS);
9981 }
9982 
9983 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
9984 {
9985   VecScatter ctx;
9986 
9987   PetscFunctionBegin;
9988   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
9989   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
9990   PetscCall(VecScatterDestroy(&ctx));
9991   PetscFunctionReturn(PETSC_SUCCESS);
9992 }
9993 
9994 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[])
9995 {
9996   const PetscInt Nc = uOff[1] - uOff[0];
9997   PetscInt       c;
9998   for (c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
9999 }
10000 
10001 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
10002 {
10003   DM           dmc;
10004   PetscDS      ds;
10005   Vec          ones, locmass;
10006   IS           cellIS;
10007   PetscFormKey key;
10008   PetscInt     depth;
10009 
10010   PetscFunctionBegin;
10011   PetscCall(DMClone(dm, &dmc));
10012   PetscCall(DMCopyDisc(dm, dmc));
10013   PetscCall(DMGetDS(dmc, &ds));
10014   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
10015   PetscCall(DMCreateGlobalVector(dmc, mass));
10016   PetscCall(DMGetLocalVector(dmc, &ones));
10017   PetscCall(DMGetLocalVector(dmc, &locmass));
10018   PetscCall(DMPlexGetDepth(dmc, &depth));
10019   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
10020   PetscCall(VecSet(locmass, 0.0));
10021   PetscCall(VecSet(ones, 1.0));
10022   key.label = NULL;
10023   key.value = 0;
10024   key.field = 0;
10025   key.part  = 0;
10026   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
10027   PetscCall(ISDestroy(&cellIS));
10028   PetscCall(VecSet(*mass, 0.0));
10029   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
10030   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
10031   PetscCall(DMRestoreLocalVector(dmc, &ones));
10032   PetscCall(DMRestoreLocalVector(dmc, &locmass));
10033   PetscCall(DMDestroy(&dmc));
10034   PetscFunctionReturn(PETSC_SUCCESS);
10035 }
10036 
10037 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
10038 {
10039   PetscSection gsc, gsf;
10040   PetscInt     m, n;
10041   void        *ctx;
10042   DM           cdm;
10043   PetscBool    regular;
10044 
10045   PetscFunctionBegin;
10046   if (dmFine == dmCoarse) {
10047     DM            dmc;
10048     PetscDS       ds;
10049     PetscWeakForm wf;
10050     Vec           u;
10051     IS            cellIS;
10052     PetscFormKey  key;
10053     PetscInt      depth;
10054 
10055     PetscCall(DMClone(dmFine, &dmc));
10056     PetscCall(DMCopyDisc(dmFine, dmc));
10057     PetscCall(DMGetDS(dmc, &ds));
10058     PetscCall(PetscDSGetWeakForm(ds, &wf));
10059     PetscCall(PetscWeakFormClear(wf));
10060     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
10061     PetscCall(DMCreateMatrix(dmc, mass));
10062     PetscCall(DMGetLocalVector(dmc, &u));
10063     PetscCall(DMPlexGetDepth(dmc, &depth));
10064     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
10065     PetscCall(MatZeroEntries(*mass));
10066     key.label = NULL;
10067     key.value = 0;
10068     key.field = 0;
10069     key.part  = 0;
10070     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
10071     PetscCall(ISDestroy(&cellIS));
10072     PetscCall(DMRestoreLocalVector(dmc, &u));
10073     PetscCall(DMDestroy(&dmc));
10074   } else {
10075     PetscCall(DMGetGlobalSection(dmFine, &gsf));
10076     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10077     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10078     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10079 
10080     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
10081     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10082     PetscCall(MatSetType(*mass, dmCoarse->mattype));
10083     PetscCall(DMGetApplicationContext(dmFine, &ctx));
10084 
10085     PetscCall(DMGetCoarseDM(dmFine, &cdm));
10086     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10087     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
10088     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
10089   }
10090   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
10091   PetscFunctionReturn(PETSC_SUCCESS);
10092 }
10093 
10094 /*@
10095   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10096 
10097   Input Parameter:
10098 . dm - The `DMPLEX` object
10099 
10100   Output Parameter:
10101 . regular - The flag
10102 
10103   Level: intermediate
10104 
10105 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
10106 @*/
10107 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
10108 {
10109   PetscFunctionBegin;
10110   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10111   PetscAssertPointer(regular, 2);
10112   *regular = ((DM_Plex *)dm->data)->regularRefinement;
10113   PetscFunctionReturn(PETSC_SUCCESS);
10114 }
10115 
10116 /*@
10117   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10118 
10119   Input Parameters:
10120 + dm      - The `DMPLEX` object
10121 - regular - The flag
10122 
10123   Level: intermediate
10124 
10125 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
10126 @*/
10127 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
10128 {
10129   PetscFunctionBegin;
10130   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10131   ((DM_Plex *)dm->data)->regularRefinement = regular;
10132   PetscFunctionReturn(PETSC_SUCCESS);
10133 }
10134 
10135 /*@
10136   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
10137   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
10138 
10139   Not Collective
10140 
10141   Input Parameter:
10142 . dm - The `DMPLEX` object
10143 
10144   Output Parameters:
10145 + anchorSection - If not `NULL`, set to the section describing which points anchor the constrained points.
10146 - anchorIS      - If not `NULL`, set to the list of anchors indexed by `anchorSection`
10147 
10148   Level: intermediate
10149 
10150 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
10151 @*/
10152 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
10153 {
10154   DM_Plex *plex = (DM_Plex *)dm->data;
10155 
10156   PetscFunctionBegin;
10157   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10158   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
10159   if (anchorSection) *anchorSection = plex->anchorSection;
10160   if (anchorIS) *anchorIS = plex->anchorIS;
10161   PetscFunctionReturn(PETSC_SUCCESS);
10162 }
10163 
10164 /*@
10165   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.
10166 
10167   Collective
10168 
10169   Input Parameters:
10170 + dm            - The `DMPLEX` object
10171 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
10172                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10173 - anchorIS      - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10174 
10175   Level: intermediate
10176 
10177   Notes:
10178   Unlike boundary conditions, when a point's degrees of freedom in a section are constrained to
10179   an outside value, the anchor constraints set a point's degrees of freedom to be a linear
10180   combination of other points' degrees of freedom.
10181 
10182   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
10183   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
10184 
10185   The reference counts of `anchorSection` and `anchorIS` are incremented.
10186 
10187 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
10188 @*/
10189 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
10190 {
10191   DM_Plex    *plex = (DM_Plex *)dm->data;
10192   PetscMPIInt result;
10193 
10194   PetscFunctionBegin;
10195   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10196   if (anchorSection) {
10197     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
10198     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
10199     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
10200   }
10201   if (anchorIS) {
10202     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
10203     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
10204     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
10205   }
10206 
10207   PetscCall(PetscObjectReference((PetscObject)anchorSection));
10208   PetscCall(PetscSectionDestroy(&plex->anchorSection));
10209   plex->anchorSection = anchorSection;
10210 
10211   PetscCall(PetscObjectReference((PetscObject)anchorIS));
10212   PetscCall(ISDestroy(&plex->anchorIS));
10213   plex->anchorIS = anchorIS;
10214 
10215   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
10216     PetscInt        size, a, pStart, pEnd;
10217     const PetscInt *anchors;
10218 
10219     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10220     PetscCall(ISGetLocalSize(anchorIS, &size));
10221     PetscCall(ISGetIndices(anchorIS, &anchors));
10222     for (a = 0; a < size; a++) {
10223       PetscInt p;
10224 
10225       p = anchors[a];
10226       if (p >= pStart && p < pEnd) {
10227         PetscInt dof;
10228 
10229         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10230         if (dof) {
10231           PetscCall(ISRestoreIndices(anchorIS, &anchors));
10232           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
10233         }
10234       }
10235     }
10236     PetscCall(ISRestoreIndices(anchorIS, &anchors));
10237   }
10238   /* reset the generic constraints */
10239   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
10240   PetscFunctionReturn(PETSC_SUCCESS);
10241 }
10242 
10243 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
10244 {
10245   PetscSection anchorSection;
10246   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
10247 
10248   PetscFunctionBegin;
10249   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10250   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10251   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
10252   PetscCall(PetscSectionGetNumFields(section, &numFields));
10253   if (numFields) {
10254     PetscInt f;
10255     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
10256 
10257     for (f = 0; f < numFields; f++) {
10258       PetscInt numComp;
10259 
10260       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
10261       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
10262     }
10263   }
10264   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10265   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10266   pStart = PetscMax(pStart, sStart);
10267   pEnd   = PetscMin(pEnd, sEnd);
10268   pEnd   = PetscMax(pStart, pEnd);
10269   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
10270   for (p = pStart; p < pEnd; p++) {
10271     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10272     if (dof) {
10273       PetscCall(PetscSectionGetDof(section, p, &dof));
10274       PetscCall(PetscSectionSetDof(*cSec, p, dof));
10275       for (f = 0; f < numFields; f++) {
10276         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
10277         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
10278       }
10279     }
10280   }
10281   PetscCall(PetscSectionSetUp(*cSec));
10282   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
10283   PetscFunctionReturn(PETSC_SUCCESS);
10284 }
10285 
10286 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
10287 {
10288   PetscSection    aSec;
10289   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
10290   const PetscInt *anchors;
10291   PetscInt        numFields, f;
10292   IS              aIS;
10293   MatType         mtype;
10294   PetscBool       iscuda, iskokkos;
10295 
10296   PetscFunctionBegin;
10297   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10298   PetscCall(PetscSectionGetStorageSize(cSec, &m));
10299   PetscCall(PetscSectionGetStorageSize(section, &n));
10300   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
10301   PetscCall(MatSetSizes(*cMat, m, n, m, n));
10302   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
10303   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
10304   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
10305   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
10306   if (iscuda) mtype = MATSEQAIJCUSPARSE;
10307   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
10308   else mtype = MATSEQAIJ;
10309   PetscCall(MatSetType(*cMat, mtype));
10310   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
10311   PetscCall(ISGetIndices(aIS, &anchors));
10312   /* cSec will be a subset of aSec and section */
10313   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
10314   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10315   PetscCall(PetscMalloc1(m + 1, &i));
10316   i[0] = 0;
10317   PetscCall(PetscSectionGetNumFields(section, &numFields));
10318   for (p = pStart; p < pEnd; p++) {
10319     PetscInt rDof, rOff, r;
10320 
10321     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10322     if (!rDof) continue;
10323     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10324     if (numFields) {
10325       for (f = 0; f < numFields; f++) {
10326         annz = 0;
10327         for (r = 0; r < rDof; r++) {
10328           a = anchors[rOff + r];
10329           if (a < sStart || a >= sEnd) continue;
10330           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10331           annz += aDof;
10332         }
10333         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10334         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
10335         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10336       }
10337     } else {
10338       annz = 0;
10339       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10340       for (q = 0; q < dof; q++) {
10341         a = anchors[rOff + q];
10342         if (a < sStart || a >= sEnd) continue;
10343         PetscCall(PetscSectionGetDof(section, a, &aDof));
10344         annz += aDof;
10345       }
10346       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10347       PetscCall(PetscSectionGetOffset(cSec, p, &off));
10348       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10349     }
10350   }
10351   nnz = i[m];
10352   PetscCall(PetscMalloc1(nnz, &j));
10353   offset = 0;
10354   for (p = pStart; p < pEnd; p++) {
10355     if (numFields) {
10356       for (f = 0; f < numFields; f++) {
10357         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10358         for (q = 0; q < dof; q++) {
10359           PetscInt rDof, rOff, r;
10360           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10361           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10362           for (r = 0; r < rDof; r++) {
10363             PetscInt s;
10364 
10365             a = anchors[rOff + r];
10366             if (a < sStart || a >= sEnd) continue;
10367             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10368             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
10369             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10370           }
10371         }
10372       }
10373     } else {
10374       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10375       for (q = 0; q < dof; q++) {
10376         PetscInt rDof, rOff, r;
10377         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10378         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10379         for (r = 0; r < rDof; r++) {
10380           PetscInt s;
10381 
10382           a = anchors[rOff + r];
10383           if (a < sStart || a >= sEnd) continue;
10384           PetscCall(PetscSectionGetDof(section, a, &aDof));
10385           PetscCall(PetscSectionGetOffset(section, a, &aOff));
10386           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10387         }
10388       }
10389     }
10390   }
10391   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
10392   PetscCall(PetscFree(i));
10393   PetscCall(PetscFree(j));
10394   PetscCall(ISRestoreIndices(aIS, &anchors));
10395   PetscFunctionReturn(PETSC_SUCCESS);
10396 }
10397 
10398 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
10399 {
10400   DM_Plex     *plex = (DM_Plex *)dm->data;
10401   PetscSection anchorSection, section, cSec;
10402   Mat          cMat;
10403 
10404   PetscFunctionBegin;
10405   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10406   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10407   if (anchorSection) {
10408     PetscInt Nf;
10409 
10410     PetscCall(DMGetLocalSection(dm, &section));
10411     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
10412     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
10413     PetscCall(DMGetNumFields(dm, &Nf));
10414     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
10415     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
10416     PetscCall(PetscSectionDestroy(&cSec));
10417     PetscCall(MatDestroy(&cMat));
10418   }
10419   PetscFunctionReturn(PETSC_SUCCESS);
10420 }
10421 
10422 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10423 {
10424   IS           subis;
10425   PetscSection section, subsection;
10426 
10427   PetscFunctionBegin;
10428   PetscCall(DMGetLocalSection(dm, &section));
10429   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10430   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10431   /* Create subdomain */
10432   PetscCall(DMPlexFilter(dm, label, value, subdm));
10433   /* Create submodel */
10434   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10435   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10436   PetscCall(DMSetLocalSection(*subdm, subsection));
10437   PetscCall(PetscSectionDestroy(&subsection));
10438   PetscCall(DMCopyDisc(dm, *subdm));
10439   /* Create map from submodel to global model */
10440   if (is) {
10441     PetscSection    sectionGlobal, subsectionGlobal;
10442     IS              spIS;
10443     const PetscInt *spmap;
10444     PetscInt       *subIndices;
10445     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10446     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10447 
10448     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10449     PetscCall(ISGetIndices(spIS, &spmap));
10450     PetscCall(PetscSectionGetNumFields(section, &Nf));
10451     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10452     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10453     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10454     for (p = pStart; p < pEnd; ++p) {
10455       PetscInt gdof, pSubSize = 0;
10456 
10457       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10458       if (gdof > 0) {
10459         for (f = 0; f < Nf; ++f) {
10460           PetscInt fdof, fcdof;
10461 
10462           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10463           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10464           pSubSize += fdof - fcdof;
10465         }
10466         subSize += pSubSize;
10467         if (pSubSize) {
10468           if (bs < 0) {
10469             bs = pSubSize;
10470           } else if (bs != pSubSize) {
10471             /* Layout does not admit a pointwise block size */
10472             bs = 1;
10473           }
10474         }
10475       }
10476     }
10477     /* Must have same blocksize on all procs (some might have no points) */
10478     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
10479     bsLocal[1] = bs;
10480     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10481     if (bsMinMax[0] != bsMinMax[1]) {
10482       bs = 1;
10483     } else {
10484       bs = bsMinMax[0];
10485     }
10486     PetscCall(PetscMalloc1(subSize, &subIndices));
10487     for (p = pStart; p < pEnd; ++p) {
10488       PetscInt gdof, goff;
10489 
10490       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10491       if (gdof > 0) {
10492         const PetscInt point = spmap[p];
10493 
10494         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10495         for (f = 0; f < Nf; ++f) {
10496           PetscInt fdof, fcdof, fc, f2, poff = 0;
10497 
10498           /* Can get rid of this loop by storing field information in the global section */
10499           for (f2 = 0; f2 < f; ++f2) {
10500             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10501             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10502             poff += fdof - fcdof;
10503           }
10504           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10505           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10506           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10507         }
10508       }
10509     }
10510     PetscCall(ISRestoreIndices(spIS, &spmap));
10511     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10512     if (bs > 1) {
10513       /* We need to check that the block size does not come from non-contiguous fields */
10514       PetscInt i, j, set = 1;
10515       for (i = 0; i < subSize; i += bs) {
10516         for (j = 0; j < bs; ++j) {
10517           if (subIndices[i + j] != subIndices[i] + j) {
10518             set = 0;
10519             break;
10520           }
10521         }
10522       }
10523       if (set) PetscCall(ISSetBlockSize(*is, bs));
10524     }
10525     /* Attach nullspace */
10526     for (f = 0; f < Nf; ++f) {
10527       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10528       if ((*subdm)->nullspaceConstructors[f]) break;
10529     }
10530     if (f < Nf) {
10531       MatNullSpace nullSpace;
10532       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10533 
10534       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10535       PetscCall(MatNullSpaceDestroy(&nullSpace));
10536     }
10537   }
10538   PetscFunctionReturn(PETSC_SUCCESS);
10539 }
10540 
10541 /*@
10542   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10543 
10544   Input Parameters:
10545 + dm    - The `DM`
10546 - dummy - unused argument
10547 
10548   Options Database Key:
10549 . -dm_plex_monitor_throughput - Activate the monitor
10550 
10551   Level: developer
10552 
10553 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10554 @*/
10555 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10556 {
10557   PetscLogHandler default_handler;
10558 
10559   PetscFunctionBegin;
10560   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10561   PetscCall(PetscLogGetDefaultHandler(&default_handler));
10562   if (default_handler) {
10563     PetscLogEvent      event;
10564     PetscEventPerfInfo eventInfo;
10565     PetscReal          cellRate, flopRate;
10566     PetscInt           cStart, cEnd, Nf, N;
10567     const char        *name;
10568 
10569     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10570     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10571     PetscCall(DMGetNumFields(dm, &Nf));
10572     PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10573     PetscCall(PetscLogEventGetPerfInfo(PETSC_DEFAULT, event, &eventInfo));
10574     N        = (cEnd - cStart) * Nf * eventInfo.count;
10575     flopRate = eventInfo.flops / eventInfo.time;
10576     cellRate = N / eventInfo.time;
10577     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)));
10578   } else {
10579     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.");
10580   }
10581   PetscFunctionReturn(PETSC_SUCCESS);
10582 }
10583